Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions packages/products/src/markov-chains/MainView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,67 @@
import MarkovChainInfo from "./ui/MarkovChainInfo.vue";
import MarkovChainInfoLabels from "./ui/MarkovChainInfoLabels.vue";
import { useMarkovColorizer } from "./ui/useMarkovColorizer";
import { watch } from "vue";
import GButton from "@magic/ui/graph/button/GButton.vue";
import gsap from "gsap";
import colors from "@magic/utils/colors";

const graphWithCanvas = useGraphWithCanvas(MARKOV_CHAIN_GRAPH_SETTINGS);
const { graph } = graphWithCanvas;

const markov = useMarkovChain(graph);
useMarkovColorizer(graph, markov).colorize();

const int = gsap.utils.interpolate(colors.RED_500, colors.RED_800);

const { play, stop } = graph.defineTimeline({
forShapes: ["circle"],
durationMs: 2000,
customInterpolations: {
stroke: {
value: (progress, schema) => ({
color: progress < 0.5 ? int(progress * 2) : int(2 - progress * 2),
lineWidth: schema.stroke?.lineWidth ?? 10,
}),
easing: "in-out",
},
},
synchronize: true,
});

graph.subscribe("onFocusChange", (newIds, oldIds) => {
const newNodeIds = Array.from(newIds).filter(graph.getNode);
const oldNodeIds = Array.from(oldIds).filter(graph.getNode);
newNodeIds.forEach((nodeId) => {
stop({ shapeId: nodeId });
});
const noLongerFocused = Array.from(oldNodeIds).filter(
(nodeId) => !newNodeIds.includes(nodeId)
);
for (const nodeId of noLongerFocused) {
if (markov.invalidStates.value.has(nodeId)) play({ shapeId: nodeId });
}
});

watch(markov.invalidStates, () => {
for (const node of graph.nodes.value) {
stop({ shapeId: node.id });
if (markov.invalidStates.value.has(node.id)) play({ shapeId: node.id });
}
});

const test = () => {
for (const nodeId of markov.invalidStates.value) {
play({ shapeId: nodeId });
}
};
</script>

<template>
<GraphProduct v-bind="graphWithCanvas">
<template #top-center>
<MarkovChainInfo :markov="markov" />
<GButton @click="test">Test</GButton>
</template>

<template #bottom-center>
Expand Down
1 change: 1 addition & 0 deletions packages/shapes/src/animation/autoAnimate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { DefineTimeline } from './timeline/define';
import type { LooseSchema, LooseSchemaValue } from './types';

export const AUTO_ANIMATE_DURATION_MS = 500;
// properties supported by the auto animate feature
const AUTO_ANIMATED_PROPERTIES = new Set([
'at',
'start',
Expand Down
6 changes: 3 additions & 3 deletions packages/shapes/src/animation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ export const useAnimatedShapes = () => {
const schemaIdToShapeName: Map<SchemaId, ShapeName> = new Map();

const { defineTimeline, timelineIdToTimeline } = useDefineTimeline({
play: ({ shapeId, timelineId, runCount = Infinity }) => {
play: ({ shapeId, timelineId, synchronize, runCount = Infinity }) => {
const newAnimation: ActiveAnimation = {
runCount,
startedAt: Date.now(),
runCount: synchronize ? Infinity : runCount,
startedAt: synchronize ? 0 : Date.now(),
timelineId,
};

Expand Down
10 changes: 8 additions & 2 deletions packages/shapes/src/animation/timeline/define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ type IdField = {
type TimelinePlayOptions = ShapeTarget & {
/**
* number of times this animation should run
*
* 👉 NOTE 👈 if {@link Timeline.synchronize} is set to true, `runCount` will always be `Infinity`
* @default Infinity
*/
runCount?: number;
};

export type UseDefineTimelineOptions = {
play: (options: TimelinePlayOptions & IdField) => void;
play: (options: TimelinePlayOptions & IdField & Pick<Timeline<any>, 'synchronize'>) => void;
pause: (options: ShapeTarget & IdField) => void;
resume: (options: ShapeTarget & IdField) => void;
stop: (options: ShapeTarget & IdField) => void;
Expand Down Expand Up @@ -126,6 +128,10 @@ export type Timeline<T extends keyof ShapeNameToSchema> = DeepReadonly<
forShapes: T[];
keyframes?: TimelineKeyframe<SchemaWithDefaults[NoInfer<T>]>[];
easing?: Partial<Record<keyof SchemaWithDefaults[NoInfer<T>], EasingOption>>;
/**
* allow shapes to animate in sync even when invoking {@link TimelineControls.play} at different times
*/
synchronize?: boolean;
} & TimelinePlaybackDuration &
TimelinePlaybackDelay &
TimelineCustomInterpolations<SchemaWithDefaults[NoInfer<T>]>
Expand All @@ -143,7 +149,7 @@ export const useDefineTimeline = (controls: UseDefineTimelineOptions) => {
timelineIdToTimeline.set(timelineId, compiledTimeline);

return {
play: (opts) => controls.play({ ...opts, timelineId }),
play: (opts) => controls.play({ ...opts, timelineId, synchronize: timeline.synchronize }),
pause: (opts) => controls.pause({ ...opts, timelineId }),
resume: (opts) => controls.resume({ ...opts, timelineId }),
stop: (opts) => controls.stop({ ...opts, timelineId }),
Expand Down
Loading