|
1 | 1 | import { createGroup, createRect } from '@visactor/vrender-core'; |
2 | 2 | import { registerAnimate } from '../../src/register'; |
3 | 3 | import { registerCustomAnimate } from '../../src/custom/register'; |
| 4 | +import { AnimateExecutor } from '../../src/executor/animate-executor'; |
4 | 5 | import { DefaultTimeline } from '../../src/timeline'; |
5 | 6 | import { ManualTicker } from '../../src/ticker/manual-ticker'; |
6 | 7 |
|
@@ -159,6 +160,96 @@ describe('D3 pre-handoff animation runtime', () => { |
159 | 160 | expect((rect as any).baseAttributes.x).toBe(80); |
160 | 161 | }); |
161 | 162 |
|
| 163 | + test('wait step holds the previous frame without committing it to baseAttributes', () => { |
| 164 | + const { group, ticker, graphicService } = createStageHarness('wait-step'); |
| 165 | + const rect = createAnimatedRect(graphicService); |
| 166 | + group.appendChild(rect); |
| 167 | + |
| 168 | + rect.animate().to({ x: 100 }, 100, 'linear').wait(50).to({ x: 200 }, 100, 'linear'); |
| 169 | + |
| 170 | + tick(ticker, 100); |
| 171 | + expect(rect.attribute.x).toBeCloseTo(100, 5); |
| 172 | + expect((rect as any).baseAttributes.x).toBe(0); |
| 173 | + |
| 174 | + tick(ticker, 1); |
| 175 | + expect(rect.attribute.x).toBeCloseTo(100, 5); |
| 176 | + expect((rect as any).baseAttributes.x).toBe(0); |
| 177 | + }); |
| 178 | + |
| 179 | + test('loop reset applies the start frame without overwriting current static truth', () => { |
| 180 | + const { group, ticker, graphicService } = createStageHarness('loop-reset'); |
| 181 | + const rect = createAnimatedRect(graphicService); |
| 182 | + group.appendChild(rect); |
| 183 | + |
| 184 | + rect.animate().to({ x: 100 }, 100, 'linear').loop(1); |
| 185 | + |
| 186 | + tick(ticker, 50); |
| 187 | + expect(rect.attribute.x).toBeCloseTo(50, 5); |
| 188 | + rect.setAttribute('x', 20); |
| 189 | + expect((rect as any).baseAttributes.x).toBe(20); |
| 190 | + |
| 191 | + tick(ticker, 50); |
| 192 | + expect(rect.attribute.x).toBe(0); |
| 193 | + expect((rect as any).baseAttributes.x).toBe(20); |
| 194 | + }); |
| 195 | + |
| 196 | + test('stop with an explicit target is a static commit API', () => { |
| 197 | + const { group, ticker, graphicService } = createStageHarness('stop-commit'); |
| 198 | + const endRect = createAnimatedRect(graphicService); |
| 199 | + const startRect = createAnimatedRect(graphicService); |
| 200 | + const customRect = createAnimatedRect(graphicService); |
| 201 | + group.appendChild(endRect); |
| 202 | + group.appendChild(startRect); |
| 203 | + group.appendChild(customRect); |
| 204 | + |
| 205 | + const endAnimate = endRect.animate().to({ x: 100 }, 100, 'linear'); |
| 206 | + tick(ticker, 50); |
| 207 | + endAnimate.stop('end'); |
| 208 | + expect(endRect.attribute.x).toBe(100); |
| 209 | + expect((endRect as any).baseAttributes.x).toBe(100); |
| 210 | + |
| 211 | + const startAnimate = startRect.animate().to({ x: 100 }, 100, 'linear'); |
| 212 | + tick(ticker, 50); |
| 213 | + startAnimate.stop('start'); |
| 214 | + expect(startRect.attribute.x).toBe(0); |
| 215 | + expect((startRect as any).baseAttributes.x).toBe(0); |
| 216 | + |
| 217 | + const customAnimate = customRect.animate().to({ x: 100 }, 100, 'linear'); |
| 218 | + tick(ticker, 50); |
| 219 | + customAnimate.stop({ x: 12 }); |
| 220 | + expect(customRect.attribute.x).toBe(12); |
| 221 | + expect((customRect as any).baseAttributes.x).toBe(12); |
| 222 | + }); |
| 223 | + |
| 224 | + test('executor attrOutChannel commits non-animated diff attrs as static truth', () => { |
| 225 | + const { group, ticker, graphicService } = createStageHarness('executor-attr-out-channel'); |
| 226 | + const rect = createAnimatedRect(graphicService); |
| 227 | + (rect as any).context = { |
| 228 | + diffAttrs: { |
| 229 | + x: 80, |
| 230 | + fill: 'red' |
| 231 | + } |
| 232 | + }; |
| 233 | + group.appendChild(rect); |
| 234 | + |
| 235 | + new AnimateExecutor(rect).execute({ |
| 236 | + channel: { |
| 237 | + x: { to: 80 } |
| 238 | + }, |
| 239 | + duration: 100, |
| 240 | + easing: 'linear' |
| 241 | + }); |
| 242 | + |
| 243 | + expect((rect as any).baseAttributes.fill).toBe('red'); |
| 244 | + expect((rect as any).baseAttributes.x).toBe(0); |
| 245 | + |
| 246 | + tick(ticker, 50); |
| 247 | + expect(rect.attribute.x).toBeCloseTo(40, 5); |
| 248 | + expect(rect.attribute.fill).toBe('red'); |
| 249 | + expect((rect as any).baseAttributes.fill).toBe('red'); |
| 250 | + expect((rect as any).baseAttributes.x).toBe(0); |
| 251 | + }); |
| 252 | + |
162 | 253 | test('switching states mid-animation restores to the new static truth and blocks late writes', () => { |
163 | 254 | const { group, ticker, graphicService } = createStageHarness('state-conflict'); |
164 | 255 | const rect = createAnimatedRect(graphicService); |
@@ -189,7 +280,7 @@ describe('D3 pre-handoff animation runtime', () => { |
189 | 280 | expect(rect.attribute.opacity).toBeCloseTo(0.8, 5); |
190 | 281 | }); |
191 | 282 |
|
192 | | - test('state switch wins over an in-flight self-driven animation on the same attribute and restores current truth', () => { |
| 283 | + test('state switch wins over an in-flight self-driven animation on the same attribute', () => { |
193 | 284 | const { group, ticker, graphicService } = createStageHarness('self-vs-state'); |
194 | 285 | const rect = createAnimatedRect(graphicService); |
195 | 286 | group.appendChild(rect); |
|
0 commit comments