Skip to content

Commit

Permalink
Feat: ScrollBox Item Proximity Event
Browse files Browse the repository at this point in the history
  • Loading branch information
bbazukun123 committed May 20, 2024
1 parent d8f40af commit 60c445a
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 2 deletions.
64 changes: 62 additions & 2 deletions src/ScrollBox.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ColorSource, Ticker, utils, Point } from '@pixi/core';
import { ColorSource, Ticker, utils, Point, Rectangle } from '@pixi/core';
import { Container, DisplayObject, IDestroyOptions } from '@pixi/display';
import { EventMode, FederatedPointerEvent } from '@pixi/events';
import { Graphics } from '@pixi/graphics';
import type { ListOptions, ListType } from './List';
import { List } from './List';
import { Trackpad } from './utils/trackpad/Trackpad';
import { Signal } from 'typed-signals';

export type ScrollBoxOptions = {
width: number;
Expand All @@ -17,8 +18,18 @@ export type ScrollBoxOptions = {
dragTrashHold?: number;
globalScroll?: boolean;
shiftScroll?: boolean;
proximityRange?: number;
} & Omit<ListOptions, 'children'>;

type ProximityEventData = {
item: Container;
index: number;
inRange: boolean;
};

const scrollerBounds = new Rectangle();
const itemBounds = new Rectangle();

/**
* Scrollable view, for arranging lists of Pixi container-based elements.
*
Expand Down Expand Up @@ -68,6 +79,12 @@ export class ScrollBox extends Container
protected dragStarTouchPoint: Point;
protected isOver = false;

protected proximityRange: number;
protected proximityCache: boolean[] = [];
private lastScrollX!: number | null;
private lastScrollY!: number | null;
public onProximityChange = new Signal<(data: ProximityEventData) => void>();

/**
* @param options
* @param {number} options.background - background color of the ScrollBox.
Expand Down Expand Up @@ -120,6 +137,8 @@ export class ScrollBox extends Container
this.__width = options.width | this.background.width;
this.__height = options.height | this.background.height;

this.proximityRange = options.proximityRange ?? 0;

if (!this.list)
{
this.list = new List();
Expand Down Expand Up @@ -179,6 +198,7 @@ export class ScrollBox extends Container
/** Remove all items from a scrollable list. */
removeItems()
{
this.proximityCache.length = 0;
this.list.removeChildren();
}

Expand All @@ -204,6 +224,7 @@ export class ScrollBox extends Container
child.eventMode = 'static';

this.list.addChild(child);
this.proximityCache.push(false);

if (!this.options.disableDynamicRendering)
{
Expand All @@ -223,7 +244,7 @@ export class ScrollBox extends Container
removeItem(itemID: number)
{
this.list.removeItem(itemID);

this.proximityCache.splice(itemID, 1);
this.resize();
}

Expand Down Expand Up @@ -733,8 +754,47 @@ export class ScrollBox extends Container
{
this.list[type] = this._trackpad[type];
}

if (this._trackpad.x !== this.lastScrollX || this._trackpad.y !== this.lastScrollY)
{
/**
* Wait a frame to ensure that the transforms of the scene graph are up-to-date.
* Since we are skipping this step on the 'getBounds' calls for performance's sake,
* this is necessary to ensure that the bounds are accurate.
*/
requestAnimationFrame(() => this.items.forEach((item, index) => this.checkItemProximity(item, index)));
this.lastScrollX = this._trackpad.x;
this.lastScrollY = this._trackpad.y;
}
}

private checkItemProximity(item: Container, index: number): void
{

Check warning on line 772 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 4 spaces but found 8

Check warning on line 772 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 4 spaces but found 8
/** Get the item bounds, capping the width and height to at least 1 for the purposes of intersection checking. */

Check warning on line 773 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 773 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12
item.getBounds(true, itemBounds);

Check warning on line 774 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 774 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12
itemBounds.width = Math.max(itemBounds.width, 1);

Check warning on line 775 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 775 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12
itemBounds.height = Math.max(itemBounds.height, 1);

Check warning on line 776 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 776 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 777 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Trailing spaces not allowed

Check warning on line 777 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Trailing spaces not allowed
// Get the scroller bounds, expanding them by the defined max distance.

Check warning on line 778 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 778 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12
this.borderMask.getBounds(true, scrollerBounds);

Check warning on line 779 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 779 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 780 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Trailing spaces not allowed

Check warning on line 780 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Trailing spaces not allowed
scrollerBounds.x -= this.proximityRange;

Check warning on line 781 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12

Check warning on line 781 in src/ScrollBox.ts

View workflow job for this annotation

GitHub Actions / build

Expected indentation of 8 spaces but found 12
scrollerBounds.y -= this.proximityRange;
scrollerBounds.width += this.proximityRange * 2;
scrollerBounds.height += this.proximityRange * 2;

// Check for intersection
const inRange = scrollerBounds.intersects(itemBounds);
const wasInRange = this.proximityCache[index];

// If the item's proximity state has changed, emit the event
if (inRange !== wasInRange)
{
this.proximityCache[index] = inRange;
this.onProximityChange.emit({ item, index, inRange });
}
}

/**
* Destroys the component.
* @param {boolean | IDestroyOptions} [options] - Options parameter.
Expand Down
120 changes: 120 additions & 0 deletions src/stories/scrollBox/ScrollBoxProximity.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { Graphics } from '@pixi/graphics';
import { Container } from '@pixi/display';
import { Text } from '@pixi/text';
import { argTypes, getDefaultArgs } from '../utils/argTypes';
import { ScrollBox } from '../../ScrollBox';
import { FancyButton } from '../../FancyButton';
import { defaultTextStyle } from '../../utils/helpers/styles';
import { action } from '@storybook/addon-actions';
import { centerElement } from '../../utils/helpers/resize';
import type { StoryFn } from '@storybook/types';

const args = {
proximityRange: 100,
width: 320,
height: 420,
radius: 20,
elementsMargin: 10,
elementsPadding: 10,
elementsWidth: 300,
elementsHeight: 80,
itemsAmount: 100,
type: [undefined, 'vertical', 'horizontal'],
fadeSpeed: 0.5,
};

const items: FancyButton[] = [];
const inRangeCache: boolean[] = [];

export const ProximityEvent: StoryFn = ({
width,
height,
radius,
elementsMargin,
elementsPadding,
elementsWidth,
elementsHeight,
itemsAmount,
proximityRange,
type,
fadeSpeed,
}: any) =>
{
const view = new Container();

const fontColor = '#000000';
const backgroundColor = '#F5E3A9';
const disableEasing = false;
const globalScroll = true;
const shiftScroll = type === 'horizontal';
const onPress = action('Button pressed');

items.length = 0;
inRangeCache.length = 0;

for (let i = 0; i < itemsAmount; i++)
{
const button = new FancyButton({
defaultView: new Graphics().beginFill(0xa5e24d).drawRoundedRect(0, 0, elementsWidth, elementsHeight, radius),
hoverView: new Graphics().beginFill(0xfec230).drawRoundedRect(0, 0, elementsWidth, elementsHeight, radius),
pressedView: new Graphics().beginFill(0xfe6048).drawRoundedRect(0, 0, elementsWidth, elementsHeight, radius),
text: new Text(`Item ${i + 1}`, {
...defaultTextStyle,
fill: fontColor
})
});

button.anchor.set(0);
button.onPress.connect(() => onPress(i + 1));
button.alpha = 0;

items.push(button);
inRangeCache.push(false);
}

const scrollBox = new ScrollBox({
background: backgroundColor,
elementsMargin,
width,
height,
radius,
padding: elementsPadding,
disableEasing,
globalScroll,
shiftScroll,
type,
proximityRange,
});

scrollBox.addItems(items);

// Handle on proximity change event.
scrollBox.onProximityChange.connect(({ index, inRange }) =>
{
inRangeCache[index] = inRange;
});

view.addChild(scrollBox);

return {
view,
resize: () => centerElement(view),
update: () =>
{
items.forEach((item, index) =>
{
const inRange = inRangeCache[index];

// Fade in/out according to whether the item is within the specified range.
if (inRange && item.alpha < 1) item.alpha += 0.04 * fadeSpeed;
else if (!inRange && item.alpha > 0) item.alpha -= 0.04 * fadeSpeed;
});
},
};
};

export default {
title: 'Components/ScrollBox/Proximity Event',
argTypes: argTypes(args),
args: getDefaultArgs(args)
};

0 comments on commit 60c445a

Please sign in to comment.