View difference between Paste ID: 1bEaweqJ and ZgU9i63f
SHOW: | | - or go back to the newest paste.
1
import { Group } from 'konva/lib/Group';
2
import { Line } from 'konva/lib/shapes/Line';
3
import { Rect } from 'konva/lib/shapes/Rect';
4
5
import { Project } from '@motion-canvas/core/lib';
6
import { all, delay, sequence, waitFor } from '@motion-canvas/core/lib/flow';
7
import type { Scene } from '@motion-canvas/core/lib/Scene';
8
import { ThreadGenerator } from '@motion-canvas/core/lib/threading';
9
import {
10
  easeInBackGenerator,
11
  linear,
12
  tween,
13
} from '@motion-canvas/core/lib/tweening';
14
15
export default function* example(scene: Scene): ThreadGenerator {
16
  yield* scene.transition();
17
18
  let shadowAngle = Math.PI / 2;
19
20
  const tweenShadow = (rect: Rect, shadow: Line) => () => {
21
    const size = rect.size();
22
23
    let corners = [
24
      { x: 0, y: 0 }, // top left
25
      { x: size.width, y: 0 }, // top right
26
      { x: size.width, y: size.height }, // bottom right
27
      { x: 0, y: size.height }, // bottom left
28
    ];
29
    const cornerRadius = rect.cornerRadius() as number;
30
    const innerCorners = corners.map(({ x, y }) => ({
31
      x: x === 0 ? cornerRadius : x - cornerRadius,
32
      y: y === 0 ? cornerRadius : y - cornerRadius,
33
    }));
34
35
    for (let i = 0; i < 4; i++) {
36
      corners[i] = rect.getAbsoluteTransform().point(corners[i]);
37
      innerCorners[i] = rect.getAbsoluteTransform().point(innerCorners[i]);
38
    }
39
40
    const getScore = ({ x, y }: { x: number; y: number }, topRight: boolean) =>
41
      (x * Math.cos(shadowAngle + Math.PI / 2) +
42
        y * Math.sin(shadowAngle + Math.PI / 2)) *
43
      (topRight ? -1 : 1);
44
45
    const bottomLeft = corners.reduce((acc, cur) =>
46
      getScore(cur, false) > getScore(acc, false) ? cur : acc,
47
    );
48
    const topRight = corners.reduce((acc, cur) =>
49
      getScore(cur, true) > getScore(acc, true) ? cur : acc,
50
    );
51
52
    const bottomLeftInner = innerCorners[corners.indexOf(bottomLeft)];
53
    const topRightInner = innerCorners[corners.indexOf(topRight)];
54
55
    const getRoundPointPos = (
56
      innerPoint: { x: number; y: number },
57
      outerPoint: { x: number; y: number },
58
      dim: 'x' | 'y',
59
    ) =>
60
      innerPoint[dim] +
61
      Math[dim === 'x' ? 'cos' : 'sin'](shadowAngle + Math.PI / 2) *
62
        (outerPoint === topRight ? -1 : 1) *
63
        cornerRadius *
64
        rect.getAbsoluteScale()[dim];
65
66
    const absolutePoints = [
67
      [
68
        getRoundPointPos(bottomLeftInner, bottomLeft, 'x'),
69
        getRoundPointPos(bottomLeftInner, bottomLeft, 'y'),
70
      ],
71
      [
72
        getRoundPointPos(topRightInner, topRight, 'x'),
73
        getRoundPointPos(topRightInner, topRight, 'y'),
74
      ],
75
    ];
76
77
    const points = absolutePoints
78
      .map(([x, y]) =>
79
        shadow.getAbsoluteTransform().copy().invert().point({ x, y }),
80
      )
81
      .map(({ x, y }) => [x, y]);
82
83
    shadow.points(
84
      points
85
        .concat(
86
          points
87
            .map(([x, y]) => [
88
              x + 2500 * Math.cos(shadowAngle),
89
              y + 2500 * Math.sin(shadowAngle),
90
            ])
91
            .reverse(),
92
        )
93
        .flat(),
94
    );
95
  };
96
97
  const COUNT = 5 ** 2;
98
  const SIZE = 50;
99
  const HALF_SIZE = SIZE / 2;
100
  const rects = [...Array(COUNT)].map(
101
    (_, i) =>
102
      new Rect({
103
        fill: '#fff',
104
        // fill: 'rgba(255,255,255,0.5)',
105
        width: SIZE,
106
        height: SIZE,
107
        x: (i - COUNT / 2 + 0.5) * SIZE,
108
        y: -600,
109
      }),
110
  );
111
112
  const rectShadows = [...Array(COUNT)].map((_, i) => {
113
    const points = [
114
      [-HALF_SIZE, HALF_SIZE],
115
      [HALF_SIZE, -HALF_SIZE],
116
    ];
117
    const LENGTH = 700;
118
119
    return new Line({
120
      fill: '#b0752e',
121
      closed: true,
122
      points: points.flat().concat(
123
        points
124
          .map(([x, y]) => [x + LENGTH, y + LENGTH])
125
          .reverse()
126
          .flat(),
127
      ),
128
    });
129
  });
130
  scene.add(...rectShadows);
131
  scene.add(...rects);
132
133
  yield tween(COUNT / 2.5, (value) => {
134
    shadowAngle = linear(value, (Math.PI * 1) / 4, (Math.PI * 7) / 4);
135
    for (let i = 0; i < rects.length; i++) {
136
      tweenShadow(rects[i], rectShadows[i])();
137
    }
138
  });
139
140
  const moveFromMiddle = (i: number, time: number) =>
141
    Math.abs(i - rects.length / 2) * time;
142
  const moveFromEdges = (i: number, time: number) =>
143
    Math.min(i, rects.length - i - 1) * time;
144
145
  yield* sequence(
146
    0.01,
147
    ...rects.map((rect, i) =>
148
      all(rect.y(0, 0.75), tween(0.75, tweenShadow(rect, rectShadows[i]))),
149
    ),
150
  );
151
  yield* all(
152
    ...rects.map((rect, i) => {
153
      const newX = (i - COUNT / 2 + 0.5) * SIZE * 1.5;
154
      return delay(
155
        moveFromEdges(i, 0.05),
156
        all(
157
          rect.x(newX, 0.75),
158
          tween(0.75, tweenShadow(rect, rectShadows[i])),
159
          rect.cornerRadius(SIZE / 10, 0.75),
160
        ),
161
      );
162
    }),
163
  );
164
165
  const COUNT_SQRT = Math.sqrt(COUNT);
166
  yield* all(
167
    ...rects.map((rect, i) => {
168
      const x = i % COUNT_SQRT;
169
      const y = Math.floor(i / COUNT_SQRT);
170
      const newX = (x - COUNT_SQRT / 2 + 0.5) * SIZE * 1.5;
171
      const newY = (y - COUNT_SQRT / 2 + 0.5) * SIZE * 1.5;
172
      return delay(
173
        moveFromMiddle(i, 0.1) - 0.3,
174
        all(
175
          rect.position({ x: newX, y: newY }, 1),
176
          tween(1, tweenShadow(rect, rectShadows[i])),
177
        ),
178
      );
179
    }),
180
  );
181
182
  yield* all(
183
    ...rects.map((rect, i) =>
184
      delay(
185
        0.01 * i,
186
        all(
187
          rect.position({ x: 0, y: 0 }, 1),
188
          tween(1, tweenShadow(rect, rectShadows[i])),
189
        ),
190
      ),
191
    ),
192
  );
193
  rects.slice(1).forEach((rect) => rect.destroy());
194
  rectShadows.slice(1).forEach((rect) => rect.destroy());
195
  const rect = rects[0];
196
  const rectShadow = rectShadows[0];
197
198
  const group = new Group();
199
  group.add(rect);
200
  scene.add(group);
201
202
  const TIME = 0.75;
203
204
  yield* all(
205
    group.position({ x: 200, y: 200 }, TIME),
206
    group.rotation(45, TIME),
207
    group.scale({ x: 3, y: 3 }, TIME),
208
    tween(TIME, tweenShadow(rect, rectShadow)),
209
  );
210
  yield* all(
211
    group.position({ x: -200, y: -100 }, TIME),
212
    group.rotation(-210, TIME),
213
    group.scale({ x: 5, y: 5 }, TIME),
214
    tween(TIME, tweenShadow(rect, rectShadow)),
215
  );
216
  yield* all(
217
    group.position({ x: 0, y: 0 }, TIME),
218
    group.rotation(-90, TIME),
219
    group.scale({ x: 2.5, y: 2.5 }, TIME),
220
    tween(TIME, tweenShadow(rect, rectShadow)),
221
  );
222
  yield* all(
223
    group.scale({ x: 0, y: 0 }, 0.75, easeInBackGenerator(5)),
224
    tween(TIME, tweenShadow(rect, rectShadow)),
225
  );
226
227
  yield* waitFor(1.0);
228
  scene.canFinish();
229
}
230