Ionic: Get the currently visible items in ion-content - angularjs

I have a long, scrollable ion-content area in my app, filled with items using a collection-repeat.
I need to know which items are visible to the user.
I cannot use the $ionicScrollDelegate.getScrollPosition to calculate the answer, because each item has a different height (item height is calculated per item).

Ended up calculating the summed heights myself of the elements, and by querying for the translateY value of the .scroll element, I can find out which item is in the visible part of the scroll.
It's reinventing the wheel, but works.
When i load the items, i call ScrollManager.setItemHeights(heights) (heights is the array of item heights in pixels), and to get the index of the currently visible item: ScrollManager.getVisibleItemIndex()
angular.module("services")
.service('ScrollManager', function() {
var getTranslateY, getVisibleItemIndex, setItemHeights, summedHeights;
summedHeights = null;
setItemHeights = function(heights) {
var height, sum, _i, _len;
summedHeights = [0];
sum = 0;
for (_i = 0, _len = heights.length; _i < _len; _i++) {
height = heights[_i];
sum += height;
summedHeights.push(sum);
}
};
// returns the style translateY of the .scroll element, in pixels
getTranslateY = function() {
return Number(document.querySelector('.scroll').style.transform.match(/,\s*(-?\d+\.?\d*)\s*/)[1]);
};
getVisibleItemIndex = function() {
var i, y;
y = -getTranslateY();
i = 0;
while (summedHeights[i] < y) {
i++;
}
return i;
};
return {
setItemHeights: setItemHeights,
getVisibleItemIndex: getVisibleItemIndex
};
});

Related

Drag and Drop Arrays AS3

I'm trying to create a drag and drop game in Actionscript 3 using mostly arrays. I'm doing it on a simpler game first before going to the main game because I need to know how the codes work first.
The simpler game is just there are two squares and two circles. The two squares are on a different array while the two circles are in the same one. What should happen is that when either circles hit (hitTestPoint) the right square, their x and y becomes the center of the square. (like it clicks to the center). And when either circles hit the left square, it should return the circles to their last position (doesn't have to be their original position).
Here's the code:
package
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.display.DisplayObject;
import flash.geom.Point;
import flash.events.Event;
public class MC_MAIN extends MovieClip
{
var mc1:mc_circle;
var mc2:mc_circle;
var mc3:mc_square;
var mc4:mc_square;
var Shapes:Array;
var Target:Array;
var WTarget:Array;
var newPlace:Point;
public function MC_MAIN()
{
// constructor code
init();
}
function init():void
{
Shapes = new Array ;
Target = new Array ;
WTarget = new Array ;
mc3 = new mc_square();
mc3.height = 75;
mc3.width = 75;
mc3.x = 400;
mc3.y = 200;
Target.push(mc3);
addChild(mc3);
mc4 = new mc_square();
mc4.height = 75;
mc4.width = 75;
mc4.x = 150;
mc4.y = 200;
WTarget.push(mc4);
addChild(mc4);
mc1 = new mc_circle();
mc1.height = 25;
mc1.width = 25;
mc1.x = 100;
mc1.y = 100;
Shapes.push(mc1);
addChild(mc1);
mc2 = new mc_circle();
mc2.height = 25;
mc2.width = 25;
mc2.x = 200;
mc2.y = 200;
Shapes.push(mc2);
addChild(mc2);
for (var i:int = 0; i<Shapes.length; i++)
{
Shapes[i].addEventListener(MouseEvent.MOUSE_DOWN, DRG);
Shapes[i].addEventListener(MouseEvent.MOUSE_UP, SDRG);
}
}
function DRG(e:MouseEvent):void
{
e.currentTarget.startDrag();
}
function SDRG(e:MouseEvent):void
{
e.currentTarget.stopDrag();
for (var m:int = 0; m<Shapes.length; m++)
{
newPlace = new Point(Shapes[m].x,Shapes[m].y);
}
trace(newPlace);
for (var a:int = 0; a<Target.length; a++)
{
for (var b:int = 0; b<Shapes.length; b++)
{
if (Target[a].hitTestPoint(Shapes[b].x,Shapes[b].y))
{
Shapes[b].x = Target[a].x;
Shapes[b].y = Target[a].y;
}
}
}
for (var c:int = 0; c<WTarget.length; c++)
{
for (var d:int = 0; d<Shapes.length; d++)
{
if (WTarget[c].hitTestPoint(Shapes[d].x,Shapes[d].y))
{
Shapes[d].x = newPlace.x;
Shapes[d].y = newPlace.y;
}
}
}
}
}
}
What happens is that the code for the left square doesn't work but their are no syntax errors. Nothing happens when either circles hit the left square.
And when I'm trying to trace the position of the circles, It just shows the x & y coordinate of only one of them. (I guess it's tracing the first object of the array which is at index 0. I'm just asking if I guessed right for this part.)
I is a bit difficult to follow the logic and there are some points that doesn't make much sense like:
for (var m:int = 0; m<Shapes.length; m++)
{
newPlace = new Point(Shapes[m].x,Shapes[m].y);
}
newPlace will be the position of the last shape in Shapes, so the loop is fairly useless.
I guess what you need is something like that:
public class MC_MAIN extends MovieClip
{
private leftSquares:Array;
private rightSquares:Array;
//more of the members from above
private startPos:Point;
//init the thing and add left and right squares
//to there respective Array
function DRG(e:MouseEvent):void
{
var t:DisplayObject = e.currentTarget;
//save the starting position
startPos = new Point(t.x,t.y);
t.startDrag();
}
function SDRG(e:MouseEvent):void {
var t:DisplayObject = e.currentTarget;
//find all squares from the left
//the target »hits«
var leftHits:Array = leftSquares.filter(
function (square:DisplayObject) {
return square.hitTestPoint(t.x, t.y);
});
//same for the right
var leftHits:Array = rightSquares.filter(
function (square:DisplayObject) {
return square.hitTestPoint(t.x, t.y);
});
//now you can apply the logic
//based on the hit Test result
//this way you can handle the case
//if it hits both, to throw an error
//or alike
if(leftHits.length > 0) {
//reset position
t.x = startPos.x;
t.y = startPos.y;
}
else if (rightHits.length > 0) {
//set the position tp the desired item in rightHits
}
else {
}
}
}
Please not that my Action Script skills haven't been used for a long time, so the code above might not compile. It is meant to illustrate the idea. Important are the following steps:
1. Save the starting position, to be able to reset it
2. Sort the `squares` in respective lists for left and right
3. Hit test both and apply the logic.

How to paint a specific cell of a stackpanel?

In order to draw the matrix of cells , I used a Stackpanel, defined by this code:
int columns = Convert.ToInt32(columnasText.Text);
int rows = Convert.ToInt32(filasText.Text);
SolidColorBrush selected1 = new SolidColorBrush(Colors.Aquamarine);
SolidColorBrush released = new SolidColorBrush(Colors.White);
for (int i = 0; i < rows; i++)
{
StackPanel stkPanel = new StackPanel();
stkPanel.Orientation = Orientation.Horizontal;
for (int j = 0; j < columns; j++)
{
Label lbl = new Label();
lbl.Height = rejilla.Height / rows;
lbl.Width = rejilla.Width / columns;
lbl.Tag = new Point(i, j);
lbl.BorderBrush = new SolidColorBrush(Colors.Black);
lbl.BorderThickness = new Thickness(1);
lbl.Background = released;
stkPanel.Children.Add(lbl);
}
rejilla.Children.Add(stkPanel);
Once is defined, I need to change the colours of each cell depending on the values of each, and I'm not able to do it.
I used your variable rejilla (I am assuming it is either StackPanel or Grid). Either way, it will work.
Method 1:
The key is using .Children.OfType()
//Get your cell location
int rowIndex = 2;
int columnIndex = 8;
//Get your desired new color
SolidColorBrush selected1 = new SolidColorBrush(Colors.Aquamarine);
//Get list of your row panels
var stackPanels = rejilla.Children.OfType<StackPanel>().ToList();
//Check if desired row panel exist
if (rowIndex < stackPanels.Count && rowIndex >= 0)
{
//Get list of your labels in the desired row panel
var labels = stackPanels[rowIndex].Children.OfType<Label>().ToList();
//Check if desired cell exist or not then change background
if (columnIndex < labels.Count && columnIndex >= 0)
labels[columnIndex].Background = selected1;
}
Method 2: Using your Tag that you set (Point)
Not recommended, this method would have been useful if you placed all your labels in one stack panel instead of placing them in multiple stack panels (one stack panel for each row).
//Get your cell location
int rowIndex = 2;
int columnIndex = 8;
//Get your desired new color
SolidColorBrush selected1 = new SolidColorBrush(Colors.Aquamarine);
//Get list of your row panels
var stackPanels = rejilla.Children.OfType<StackPanel>().ToList();
//Check if desired row panel exist
if (rowIndex < stackPanels.Count && rowIndex >= 0)
{
//Get list of your labels in the desired row panel
var label = stackPanels[rowIndex].Children.OfType<Label>()
.Where(Item => (int)(Item.Tag as Nullable<Point>).GetValueOrDefault().X == rowIndex
&& (int)(Item.Tag as Nullable<Point>).GetValueOrDefault().Y == columnIndex).FirstOrDefault();
if(label != null)
label.Background = selected1;
}
You can place the code inside a method and pass your stackPanel, color, rowIndex, columnIndex:
private void SetCellColor(StackPanel stackPanel, SolidColorBrush color, int rowIndex, int columnIndex)
{
//Get list of your row panels
var stackPanels = stackPanel.Children.OfType<StackPanel>().ToList();
//Check if desired row panel exist
if (rowIndex < stackPanels.Count && rowIndex >= 0)
{
//Get list of your labels in the desired row panel
var labels = stackPanels[rowIndex].Children.OfType<Label>().ToList();
//Check if desired cell exist or not then change background
if (columnIndex < labels.Count && columnIndex >= 0)
labels[columnIndex].Background = color;
}
}
Then call it:
SetCellColor(rejilla, new SolidColorBrush(Colors.Aquamarine), rowIndex, columnIndex);
Good Luck!

wpf radgridview How to get vertical offset of GridViewRow?

I have row index of target GridViewRow, and ScrollChanged handler callback of GridViewScrollViewer inside RadGridView. I want to get vertical offset of target row in viewport, while scrolling, if that row leaves viewport from top, return 0; if leaves from bottom, return ActualHeight of RadGridView; else, get a value same as Top offest of target row and viewport.
Here is function to get vertival offset, it will be incorrect after scrollbar moved:
//const int indicator_size = 3;
//RadGridView grid;
Func rowIndicatorOffset = (rowIndex) =>
{
double offset = (rowIndex + 1.5) * this.grid.RowHeight;
if (offset > this.grid.ActualHeight)
{
return this.grid.ActualHeight - indicator_size;
}
return offset;
};
When change vertical scroll bar offset, I want that indicator(red one in the picture above) keep same Y position with target row:
//System.Windows.Controls.ScrollChangedEventArgs offsetValues (from ScrollChanged event)
double fixRatio = (this.grid.ActualHeight - this.offsetValues.VerticalOffset) / this.grid.ActualHeight;
double offset = (rowIndex + 1.5) * this.grid.RowHeight * fixRatio;
the code above works not well as expected apparently, so what's the answer?
I got it wrong about Viewport, the diagram below may figure it out.
diagram
//const int indicator_size = 3;
//RadGridView grid;
Func<int, double> rowIndicatorOffset = (rowIndex) =>
{
double targetOffset = (rowIndex + 1.5) * this.grid.RowHeight;
double viewportOffset = this.offsetValues.VerticalOffset;
double offset = targetOffset - viewportOffset;
if (offset > this.grid.ActualHeight)
{
return this.grid.ActualHeight - indicator_size;
}
else if (offset < 0)
{
return 0;
}
else
{
return offset;
}
};

How can i capture screen region while not focusing wpf app?

I have a WPf application that monitors a certain region on screen keeping track of the count of pixels with certain colors.
What i want is the actual capturing of the region and reading of values to happen in another thread, but i cant seem to wrap my head around how to actuale grab the region while i focus another window.
Here is some code:
// Colors to keep track of
var fooColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FF6ebfe2");
var barColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFe67e6e");
// previewWindow : Rectangle Control
var X = (int)previewWindow.PointToScreen(new System.Windows.Point(0, 0)).X;
var Y = (int)previewWindow.PointToScreen(new System.Windows.Point(0, 0)).Y;
Rectangle rect = new Rectangle(X, Y, (int)previewWindow.ActualWidth, (int)previewWindow.ActualHeight);
Bitmap bmp = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var screenBmp = new Bitmap(
rect.Width,
rect.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb)
)
{
using (var bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(X, Y, 0, 0, screenBmp.Size);
BitmapSource bmpSource = Imaging.CreateBitmapSourceFromHBitmap(
screenBmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
var red = 0;
var blue = 0;
for (int i = 0; i < rect.Width; i++)
{
for (int x = 0; x < rect.Height; x++)
{
System.Windows.Media.Color color = GetPixelColor(bmpSource, i, x);
if (color == fooColor)
{
blue++;
}
else if (color == barColor)
{
red++;
}
}
}
txtbar.Text = red.ToString();
txtfoo.Text = blue.ToString();
if (red < 150)
{
ProcessBar();
}
if (blue < 150)
{
ProcessFoo();
}
}
}

caching and rendering spritesheets doesn't work

I'd like to cache my spritesheet (tilesheet) into an array. I do this because every spritesheet shall be cached inside of an array so my objects can pull their tiles from them easily. But my cache doesn't seem to work because absolutely nothing gets rendered out of it. I can't see anything.
There is something inside my cache (likely bitmapData) and it is not null so currently I don't know where the problem might be.
Can someone help me out with this issue, please?
this function shall render each tile of an array to a background via copypixels
public function renderCachedTile(canvasBitmapData:BitmapData, tileArray:Array):void
{
tileCache = tileArray;
tileArray = [];
x = nextX;
y = nextY;
point.x = x;
point.y = y;
tileRect.x = tileWidth;
tileRect.y = tileHeight;
if (animationCount >= animationDelay)
{
animationCount = 0;
if(reverse)
{
currentTile--;
if (currentTile < 1)
{
currentTile = tilesLength - 1;
}
} else {
currentTile++;
if (currentTile >= tilesLength - 1)
{
currentTile = 0;
}
}
} else {
animationCount++;
}
canvasBitmapData.lock();
canvasBitmapData.copyPixels(tileCache[currentTile], tileRect, point);
canvasBitmapData.unlock();
}
this function 'separates' my spritesheet into tiles and throws them into an array
private function tileToCache():void {
var tileBitmapData:BitmapData = new BitmapData(tileWidth, tileHeight, true, 0x00000000);
var tilePt:Point = new Point(0, 0);
var tileRect:Rectangle = new Rectangle;
tileCache = [];
for (var tileCtr:int = 0; tileCtr < tilesLength; tileCtr++) {
tileBitmapData.lock();
tileRect.x = int((tileCtr % spritesPerRow)) * tileWidth;
tileRect.y = int((tileCtr / spritesPerRow)) * tileHeight;
tileBitmapData.copyPixels(tileSheet, tileRect, tilePt);
tileBitmapData.unlock();
tileCache.push(tileBitmapData);
}
}
Unless there is code missing from your example, in your tileToCache you instantiate and use tileRect without defining a width and height:
private function tileToCache():void {
var tileRect:Rectangle = new Rectangle;
/* ... */
tileBitmapData.copyPixels(tileSheet, tileRect, tilePt);
If your source was at 0, 0 I presume you would want:
var tileRect:Rectangle = new Rectangle(0, 0, tileWidth, tileHeight);

Resources