1const px = (value) => `${value}px`; 2 3const transform = (scale, rotation) => 4 `scale(${scale}) rotate(${rotation}deg)`; 5 6const transformXY = (scaleX, scaleY, rotation) => 7 `scale(${scaleX}, ${scaleY}) rotate(${rotation}deg)`; 8 9const baseValues = (instance) => ({ 10 rotation: instance.engine.rotation ?? 0, 11 scale: instance.engine.scale ?? 1, 12 opacity: instance.engine.opacity ?? 1 13}); 14 15const animationOptions = (instance, fallbackEasing) => ({ 16 duration: instance.engine.duration, 17 easing: 18 fallbackEasing === undefined 19 ? instance.engine.easing 20 : instance.engine.easing ?? fallbackEasing 21}); 22 23StickerEngine.registerAnimation("drive", function(instance) 24{ 25 const resolve = (position) => 26 StickerEngine.resolveEnvironmentPosition(position, instance); 27 28 const origin = instance.engine.origin; 29 const destination = instance.engine.destination; 30 31 let frames; 32 33 if ( 34 origin.startsWith("enter-") && 35 destination.startsWith("exit-") 36 ) 37 { 38 const edgeStart = resolve("edge-" + origin.substring(6)); 39 const edgeEnd = resolve("edge-" + destination.substring(5)); 40 41 const horizontal = 42 Math.abs(edgeEnd.x - edgeStart.x) >= 43 Math.abs(edgeEnd.y - edgeStart.y); 44 45 const outsideStart = { ...edgeStart }; 46 const outsideEnd = { ...edgeEnd }; 47 48 if (horizontal) 49 { 50 if (edgeStart.x < edgeEnd.x) 51 { 52 outsideStart.x = -instance.engine.displayWidth; 53 outsideEnd.x = window.innerWidth; 54 } 55 else 56 { 57 outsideStart.x = window.innerWidth; 58 outsideEnd.x = -instance.engine.displayWidth; 59 } 60 } 61 else if (edgeStart.y < edgeEnd.y) 62 { 63 outsideStart.y = -instance.engine.displayHeight; 64 outsideEnd.y = window.innerHeight; 65 } 66 else 67 { 68 outsideStart.y = window.innerHeight; 69 outsideEnd.y = -instance.engine.displayHeight; 70 } 71 72 frames = [ 73 { 74 left: px(outsideStart.x), 75 top: px(outsideStart.y) 76 }, 77 { 78 left: px(edgeStart.x), 79 top: px(edgeStart.y), 80 offset: 0.15 81 }, 82 { 83 left: px(edgeEnd.x), 84 top: px(edgeEnd.y), 85 offset: 0.85 86 }, 87 { 88 left: px(outsideEnd.x), 89 top: px(outsideEnd.y) 90 } 91 ]; 92 } 93 else 94 { 95 frames = [ 96 { 97 left: px(instance.origin.x), 98 top: px(instance.origin.y) 99 }, 100 { 101 left: px(instance.destination.x), 102 top: px(instance.destination.y) 103 } 104 ]; 105 } 106 107 return { 108 frames, 109 options: animationOptions(instance) 110 }; 111}); 112 113StickerEngine.registerAnimation("launch", function(instance) 114{ 115 const start = instance.origin; 116 const end = instance.destination; 117 118 const dx = end.x - start.x; 119 const dy = end.y - start.y; 120 121 const curve = Math.min( 122 Math.max(Math.hypot(dx, dy) * 0.25, 40), 123 120 124 ); 125 126 const control = { 127 x: (start.x + end.x) / 2, 128 y: (start.y + end.y) / 2 129 }; 130 131 const length = Math.hypot(dx, dy) || 1; 132 133 control.x += (-dy / length) * curve; 134 control.y += (dx / length) * curve; 135 136 const frames = []; 137 const STEPS = 10; 138 139 for (let i = 0; i <= STEPS; i++) 140 { 141 const t = i / STEPS; 142 const u = 1 - t; 143 144 frames.push({ 145 left: px( 146 (u * u * start.x) + 147 (2 * u * t * control.x) + 148 (t * t * end.x) 149 ), 150 top: px( 151 (u * u * start.y) + 152 (2 * u * t * control.y) + 153 (t * t * end.y) 154 ), 155 offset: t 156 }); 157 } 158 159 return { 160 frames, 161 options: animationOptions(instance) 162 }; 163}); 164 165function createThrowAnimation(spinRotation = 0) 166{ 167 return function(instance) 168 { 169 const start = instance.origin; 170 const end = instance.destination; 171 172 const dx = end.x - start.x; 173 const dy = end.y - start.y; 174 const distance = Math.hypot(dx, dy) || 1; 175 176 const perpendicularX = -dy / distance; 177 const perpendicularY = dx / distance; 178 179 const frames = []; 180 const STEPS = 30; 181 182 const base = baseValues(instance); 183 184 const MAX_WOBBLE = 8 + (Math.random() * 3); 185 const MAX_ROTATION = 8 + (Math.random() * 4);
186 const WOBBLES = 1.75 + (Math.random() * 0.75); 187 const PHASE = Math.random() * Math.PI * 2; 188 189 for (let i = 0; i <= STEPS; i++) 190 { 191 const t = i / STEPS; 192 const x = start.x + (dx * t); 193 const y = start.y + (dy * t); 194 const angle = (t * Math.PI * 2 * WOBBLES) + PHASE; 195 const strength = Math.pow(t, 1.8); 196 197 const wobble = 198 Math.sin(angle) * 199 MAX_WOBBLE * 200 strength; 201 202 const rotation = 203 base.rotation + 204 (spinRotation * t) + 205 (Math.cos(angle) * MAX_ROTATION * strength); 206 207 const scale = 208 base.scale - 209 ((base.scale - 0.05) * Math.pow(t, 1.8)); 210 211 const opacity = 212 t < 0.8 213 ? base.opacity 214 : base.opacity * (1 - ((t - 0.8) / 0.2)); 215 216 frames.push({ 217 left: px(x + (perpendicularX * wobble)), 218 top: px(y + (perpendicularY * wobble)), 219 transform: transform(scale, rotation), 220 opacity, 221 offset: t 222 }); 223 } 224 225 return { 226 frames, 227 options: animationOptions(instance, "linear") 228 }; 229 }; 230} 231 232StickerEngine.registerAnimation( 233 "throw", 234 createThrowAnimation() 235); 236 237// Keep throwtate at two complete turns (720°). 238StickerEngine.registerAnimation( 239 "throwtate", 240 createThrowAnimation(720) 241); 242 243StickerEngine.registerAnimation("drop", function(instance) 244{ 245 const X_MARGIN = 200; 246 247 const start = { 248 x: instance.origin.x, 249 y: instance.origin.y 250 }; 251 252 if (instance.engine.origin === "random-top") 253 { 254 start.x = 255 X_MARGIN + 256 (Math.random() * (window.innerWidth - (X_MARGIN * 2))); 257 } 258 259 const frames = []; 260 const STEPS = 40; 261 const base = baseValues(instance); 262 263 const BOUNCES = 5; 264 const DECAY = 2.4; 265 const SQUASH = 0.08; 266 const FLOOR_Y = (window.innerHeight - instance.engine.displayHeight) / 2; 267 const DROP_HEIGHT = FLOOR_Y - start.y; 268 const FALL_TIME = 0.25; 269 const FADE_TIME = 0.85; 270 271 for (let i = 0; i <= STEPS; i++) 272 { 273 const t = i / STEPS; 274 275 let y; 276 let impact = 0; 277 let rotation = base.rotation; 278 279 if (t < FALL_TIME) 280 { 281 const p = t / FALL_TIME; 282 283 y = 284 start.y + 285 (DROP_HEIGHT * Math.pow(p, 2)); 286 } 287 else 288 { 289 const p = Math.min( 290 (t - FALL_TIME) / 291 (FADE_TIME - FALL_TIME), 292 1 293 ); 294 295 const bounce = 296 Math.abs( 297 Math.sin(p * Math.PI * BOUNCES) 298 ) * 299 (DROP_HEIGHT * 0.35) * 300 Math.pow(1 - p, DECAY); 301 302 y = FLOOR_Y - bounce; 303 304 impact = 305 Math.pow( 306 Math.abs( 307 Math.sin(p * Math.PI * BOUNCES) 308 ), 309 6 310 ) * 311 Math.pow(1 - p, 1.5); 312 313 rotation = 314 base.rotation + 315 ( 316 Math.sin(p * Math.PI * BOUNCES) * 317 4 * 318 Math.pow(1 - p, 1.5) 319 ); 320 } 321 322 const scaleX = 323 base.scale * 324 (1 + (impact * SQUASH)); 325 326 const scaleY = 327 base.scale * 328 (1 - (impact * SQUASH)); 329 330 const opacity = 331 t < FADE_TIME 332 ? base.opacity 333 : base.opacity * 334 (1 - ((t - FADE_TIME) / (1 - FADE_TIME))); 335 336 frames.push({ 337 left: px(start.x), 338 top: px(y), 339 transform: transformXY(scaleX, scaleY, rotation), 340 opacity, 341 offset: t 342 }); 343 } 344 345 return { 346 frames, 347 options: animationOptions(instance, "linear") 348 }; 349}); 350 351StickerEngine.registerAnimation("fall", function(instance) 352{ 353 const X_MARGIN = 200; 354 355 const height = 356 instance.height ?? 357 instance.element.height ?? 358 128; 359 360 const x = 361 X_MARGIN + 362 (Math.random() * (window.innerWidth - (X_MARGIN * 2))); 363 364 const start = { 365 x, 366 y: -(height + 100) 367 }; 368 369 const end = { 370 x, 371 y: 372 window.innerHeight - 373 StickerEngine.environment.bottomInset - 374 height 375 }; 376 377 const frames = []; 378 const STEPS = 40; 379 const base = baseValues(instance); 380 381 const DROP_HEIGHT = end.y - start.y; 382 const BOUNCES = 6; 383 const DECAY = 2.2; 384 const SQUASH = 0.12; 385 const FALL_TIME = 0.65; 386 const FADE_TIME = 0.90; 387 388 for (let i = 0; i <= STEPS; i++) 389 { 390 const t = i / STEPS; 391 392 let y; 393 let impact = 0; 394 let rotation = base.rotation; 395 396 if (t < FALL_TIME) 397 { 398 const p = t / FALL_TIME; 399 400 y = 401 start.y + 402 (DROP_HEIGHT * Math.pow(p, 2.4)); 403 } 404 else 405 { 406 const p = Math.min( 407 (t - FALL_TIME) / 408 (FADE_TIME - FALL_TIME), 409 1 410 ); 411 412 const bounce = 413 Math.abs( 414 Math.sin(p * Math.PI * BOUNCES) 415 ) * 416 (DROP_HEIGHT * 0.18) * 417 Math.pow(1 - p, DECAY); 418 419 y = end.y - bounce; 420 421 impact = 422 Math.pow( 423 Math.abs( 424 Math.sin(p * Math.PI * BOUNCES) 425 ), 426 6 427 ) * 428 Math.pow(1 - p, 1.4); 429 430 rotation = 431 base.rotation + 432 ( 433 Math.sin(p * Math.PI * BOUNCES) * 434 8 * 435 Math.pow(1 - p, 1.4) 436 ); 437 } 438 439 const scaleX = 440 base.scale * 441 (1 + (impact * SQUASH)); 442 443 const scaleY = 444 base.scale * 445 (1 - (impact * SQUASH)); 446 447 const opacity = 448 t < FADE_TIME 449 ? base.opacity 450 : base.opacity * 451 (1 - ((t - FADE_TIME) / (1 - FADE_TIME))); 452 453 frames.push({ 454 left: px(start.x), 455 top: px(y),
456 transform: transformXY(scaleX, scaleY, rotation), 457 opacity, 458 offset: t 459 }); 460 } 461 462 return { 463 frames, 464 options: animationOptions(instance, "linear") 465 }; 466}); 467 468StickerEngine.registerAnimation("bounce", function(instance) 469{ 470 const resolve = (position) => 471 StickerEngine.resolveEnvironmentPosition(position, instance); 472 473 let start = instance.origin; 474 let end = instance.destination; 475 476 const origin = instance.engine.origin; 477 const destination = instance.engine.destination; 478 479 if ( 480 origin.startsWith("edge-") && 481 destination.startsWith("edge-") 482 ) 483 { 484 start = resolve(origin); 485 end = resolve(destination); 486 } 487 488 const dx = end.x - start.x; 489 const dy = end.y - start.y; 490 491 const frames = []; 492 const STEPS = 40; 493 const base = baseValues(instance); 494 495 const BOUNCES = 6; 496 const BOUNCE_HEIGHT = 200; 497 const DECAY = 1.5; 498 const SQUASH = 0.08; 499 const MOTION_END = 0.99; 500 const FADE_START = 0.99; 501 502 for (let i = 0; i <= STEPS; i++) 503 { 504 const t = i / STEPS; 505 506 const motion = Math.min(t, MOTION_END); 507 const progress = motion / MOTION_END; 508 509 const x = 510 start.x + 511 (dx * progress); 512 513 const groundY = 514 start.y + 515 (dy * progress); 516 517 const bounce = 518 Math.abs( 519 Math.sin(progress * Math.PI * BOUNCES) 520 ) * 521 BOUNCE_HEIGHT * 522 Math.pow(1 - progress, DECAY); 523 524 const y = groundY - bounce; 525 526 const impact = 527 Math.pow( 528 Math.abs( 529 Math.sin(progress * Math.PI * BOUNCES) 530 ), 531 8 532 ) * 533 Math.pow(1 - progress, 1.5); 534 535 const scaleX = 536 base.scale * 537 (1 + (impact * SQUASH)); 538 539 const scaleY = 540 base.scale * 541 (1 - (impact * SQUASH)); 542 543 const rotation = 544 base.rotation + 545 ( 546 Math.sin(progress * Math.PI * BOUNCES) * 547 10 * 548 Math.pow(1 - progress, 1.5) 549 ); 550 551 const opacity = 552 t < FADE_START 553 ? base.opacity 554 : base.opacity * 555 (1 - ((t - FADE_START) / (1 - FADE_START))); 556 557 frames.push({ 558 left: px(x), 559 top: px(y), 560 transform: transformXY(scaleX, scaleY, rotation), 561 opacity, 562 offset: t 563 }); 564 } 565 566 return { 567 frames, 568 options: animationOptions(instance, "linear") 569 }; 570}); 571 572StickerEngine.registerAnimation("fly", function(instance) 573{ 574 const width = 575 instance.engine.displayWidth; 576 577 const height = 578 instance.engine.displayHeight; 579 580 const origin = 581 instance.engine.origin ?? 582 "outside-left"; 583 584 const leftToRight = 585 origin.includes("left"); 586 587 const centreY = 588 (window.innerHeight - height) / 2; 589 590 const start = { 591 x: 592 leftToRight 593 ? -width 594 : window.innerWidth + width, 595 y: centreY 596 }; 597 598 const end = { 599 x: 600 leftToRight 601 ? window.innerWidth + width 602 : -width, 603 y: centreY 604 }; 605 606 const dx = end.x - start.x; 607 608 const frames = []; 609 const STEPS = 40; 610 const base = baseValues(instance); 611 612 const AMPLITUDE = 613 15 + 614 (Math.random() * 20); 615 616 const WAVES = 617 2 + 618 (Math.random() * 2); 619 620 const PHASE = 621 Math.random() * 622 Math.PI * 623 2; 624 625 const BANK = 626 5 + 627 (Math.random() * 4); 628 629 for (let i = 0; i <= STEPS; i++) 630 { 631 const t = i / STEPS; 632 633 const x = 634 start.x + 635 (dx * t); 636 637 const angle = 638 (t * Math.PI * 2 * WAVES) + 639 PHASE; 640 641 const bob = 642 Math.sin(angle) * 643 AMPLITUDE; 644 645 const rotation = 646 base.rotation + 647 (Math.cos(angle) * BANK); 648 649 frames.push({ 650 left: px(x), 651 top: px(start.y + bob), 652 transform: transform(base.scale, rotation), 653 opacity: base.opacity, 654 offset: t 655 }); 656 } 657 658 return { 659 frames, 660 options: { 661 duration: instance.engine.duration, 662 easing: "linear" 663 } 664 }; 665}); 666 667StickerEngine.registerAnimation("zoom-out", function(instance) 668{ 669 const centre = 670 StickerEngine.resolvePosition( 671 "centre", 672 instance 673 ); 674 675 const MARGIN = 200; 676 677 const start = { 678 x: 679 ( 680 Math.random() * 681 (window.innerWidth + (MARGIN * 2)) 682 ) - 683 MARGIN, 684 685 y: 686 ( 687 Math.random() * 688 (window.innerHeight + (MARGIN * 2)) 689 ) - 690 MARGIN 691 }; 692 693 const frames = []; 694 const STEPS = 40; 695 const base = baseValues(instance); 696 697 const START_SCALE = 1.8; 698 const END_SCALE = 0.1; 699 700 const SWAY = 701 10 + 702 (Math.random() * 8); 703 704 const MAX_ROTATION = 705 8 + 706 (Math.random() * 6); 707
708 const WAVES = 709 1.5 + 710 (Math.random() * 0.5); 711 712 const PHASE = 713 Math.random() * 714 Math.PI * 715 2; 716 717 const FADE_START = 0.8; 718 719 for (let i = 0; i <= STEPS; i++) 720 { 721 const t = i / STEPS; 722 723 const angle = 724 (t * Math.PI * 2 * WAVES) + 725 PHASE; 726 727 const strength = 728 1 - t; 729 730 const sway = 731 Math.sin(angle) * 732 SWAY * 733 strength; 734 735 const rotation = 736 base.rotation + 737 ( 738 Math.cos(angle) * 739 MAX_ROTATION * 740 strength 741 ); 742 743 const x = 744 start.x + 745 ((centre.x - start.x) * t); 746 747 const y = 748 start.y + 749 ((centre.y - start.y) * t); 750 751 const scale = 752 START_SCALE + 753 ((END_SCALE - START_SCALE) * t); 754 755 const opacity = 756 t < FADE_START 757 ? base.opacity 758 : base.opacity * 759 (1 - ((t - FADE_START) / (1 - FADE_START))); 760 761 frames.push({ 762 left: px(x + sway), 763 top: px(y), 764 transform: transform(scale, rotation), 765 opacity, 766 offset: t 767 }); 768 } 769 770 return { 771 frames, 772 options: 773 animationOptions( 774 instance, 775 "ease-in-out" 776 ) 777 }; 778}); 779 780StickerEngine.registerAnimation("zoom-in", function(instance) 781{ 782 const centre = 783 StickerEngine.resolvePosition( 784 "centre", 785 instance 786 ); 787 788 const focus = { 789 x: 790 centre.x + 791 ((Math.random() - 0.5) * 300), 792 793 y: 794 centre.y + 795 ((Math.random() - 0.5) * 220) 796 }; 797 798 const frames = []; 799 const STEPS = 40; 800 const base = baseValues(instance); 801 802 // API scale remains the reference size. 803 const START_SCALE = 804 base.scale * 805 0.1; 806 807 const END_SCALE = 808 base.scale * 809 1.2; 810 811 const SWAY = 812 18 + 813 (Math.random() * 10); 814 815 const MAX_ROTATION = 816 14 + 817 (Math.random() * 8); 818 819 const WAVES = 820 1.5 + 821 (Math.random() * 0.5); 822 823 const PHASE = 824 Math.random() * 825 Math.PI * 826 2; 827 828 const FADE_END = 0.2; 829 830 for (let i = 0; i <= STEPS; i++) 831 { 832 const t = i / STEPS; 833 834 const angle = 835 (t * Math.PI * 2 * WAVES) + 836 PHASE; 837 838 const strength = 839 1 - 840 Math.pow(t, 2); 841 842 const sway = 843 Math.sin(angle) * 844 SWAY * 845 strength; 846 847 const rotation = 848 base.rotation + 849 ( 850 Math.cos(angle) * 851 MAX_ROTATION * 852 strength 853 ); 854 855 const zoom = 856 1 - 857 Math.pow(1 - t, 4); 858 859 let scale = 860 START_SCALE + 861 ( 862 (END_SCALE - START_SCALE) * 863 zoom 864 ); 865 866 if (t > 0.82) 867 { 868 const p = 869 (t - 0.82) / 870 0.18; 871 872 scale += 873 Math.sin(p * Math.PI * 4) * 874 (1 - p) * 875 (base.scale * 0.22); 876 } 877 878 const opacity = 879 t < FADE_END 880 ? base.opacity * (t / FADE_END) 881 : base.opacity; 882 883 frames.push({ 884 left: px(focus.x + sway), 885 top: px(focus.y), 886 transform: transform(scale, rotation), 887 opacity, 888 offset: t 889 }); 890 } 891 892 return { 893 frames, 894 895 collision(instance) 896 { 897 const rect = 898 instance.element.getBoundingClientRect(); 899 900 return { 901 type: "circle", 902 903 x: 904 rect.left + 905 (rect.width / 2), 906 907 y: 908 rect.top + 909 (rect.height / 2), 910 911 radius: 24 912 }; 913 }, 914 915 collidesWith(other) 916 { 917 return other.engine.animation !== "zoom-in"; 918 }, 919 920 options: 921 animationOptions( 922 instance, 923 "ease-in-out" 924 ) 925 }; 926}); 927 928StickerEngine.registerAnimation("shake", function(instance) 929{ 930 const centre = 931 StickerEngine.resolvePosition( 932 "centre", 933 instance 934 ); 935 936 const width = 937 instance.engine.displayWidth; 938 939 const height = 940 instance.engine.displayHeight; 941 942 const start = { 943 x: 944 Math.random() * 945 Math.max( 946 window.innerWidth - width, 947 0 948 ), 949 950 y: 951 Math.random() * 952 Math.max( 953 window.innerHeight - height, 954 0 955 ) 956 }; 957 958 const frames = []; 959 const STEPS = 60; 960 const base = baseValues(instance); 961 962 const ARRIVE_END = 0.28; 963 const SHAKE_END = 0.82; 964 const POP_PEAK = 0.90; 965 966 const START_SCALE = 0.1; 967 const SHAKE_DISTANCE = 30; 968 const SHAKE_ROTATION = 9; 969 const SHAKE_FREQUENCY = 12; 970 const POP_SCALE = base.scale * 1.5; 971 const END_SCALE = 0.05; 972 973 for (let i = 0; i <= STEPS; i++) 974 { 975 const t = i / STEPS; 976 977 let x; 978 let y; 979 let scale; 980 let rotation; 981 let opacity = base.opacity; 982 983 if (t < ARRIVE_END) 984 { 985 const p = 986 t / 987 ARRIVE_END; 988 989 const movement = 990 1 - 991 Math.pow(1 - p, 4); 992 993 x = 994 start.x + 995 ((centre.x - start.x) * movement); 996 997 y = 998 start.y + 999 ((centre.y - start.y) * movement); 1000 1001 scale = 1002 START_SCALE + 1003 ((base.scale - START_SCALE) * movement); 1004 1005 rotation = 1006 base.rotation + 1007 ( 1008 Math.sin(p * Math.PI * 2) * 1009 12 * 1010 (1 - p) 1011 ); 1012 } 1013 else if (t < SHAKE_END) 1014 { 1015 const p = 1016 (t - ARRIVE_END) / 1017 (SHAKE_END - ARRIVE_END); 1018 1019 const angle = 1020 p * 1021 Math.PI * 1022 2 * 1023 SHAKE_FREQUENCY; 1024 1025 const strength = 1026 p < 0.8 1027 ? 1 1028 : 1 - ((p - 0.8) / 0.2); 1029 1030 x = 1031 centre.x + 1032 ( 1033 Math.sin(angle) * 1034 SHAKE_DISTANCE * 1035 strength 1036 ); 1037 1038 y = 1039 centre.y + 1040 ( 1041 Math.sin(angle * 0.5) * 1042 3 * 1043 strength 1044 ); 1045 1046 scale = 1047 base.scale + 1048 ( 1049 Math.abs(Math.sin(angle)) * 1050 0.035 * 1051 strength 1052 ); 1053 1054 rotation = 1055 base.rotation + 1056 ( 1057 Math.cos(angle) * 1058 SHAKE_ROTATION * 1059 strength 1060 ); 1061 } 1062 else if (t < POP_PEAK) 1063 { 1064 const p = 1065 (t - SHAKE_END) / 1066 (POP_PEAK - SHAKE_END); 1067 1068 const eased = 1069 1 - 1070 Math.pow(1 - p, 3); 1071 1072 x = centre.x; 1073 y = centre.y; 1074 1075 scale = 1076 base.scale + 1077 ( 1078 (POP_SCALE - base.scale) * 1079 eased 1080 ); 1081 1082 rotation = 1083 base.rotation + 1084 (p * 6); 1085 } 1086 else 1087 { 1088 const p = 1089 (t - POP_PEAK) / 1090 (1 - POP_PEAK); 1091 1092 x = centre.x; 1093 y = centre.y; 1094 1095 scale = 1096 POP_SCALE + 1097 ( 1098 (END_SCALE - POP_SCALE) * 1099 Math.pow(p, 2) 1100 ); 1101 1102 rotation = 1103 base.rotation + 1104 6 + 1105 (p * 10); 1106 1107 opacity = 1108 base.opacity * 1109 (1 - p); 1110 } 1111 1112 frames.push({ 1113 left: px(x), 1114 top: px(y), 1115 transform: transform(scale, rotation), 1116 opacity, 1117 offset: t 1118 }); 1119 } 1120 1121 return { 1122 frames, 1123 1124 options: { 1125 duration: instance.engine.duration, 1126 easing: "linear" 1127 } 1128 }; 1129}); 1130 1131StickerEngine.registerAnimation("swirl", function(instance) 1132{ 1133 const centre = 1134 StickerEngine.resolvePosition( 1135 "centre", 1136 instance 1137 ); 1138 1139 const width = 1140 instance.engine.displayWidth; 1141 1142 const height = 1143 instance.engine.displayHeight; 1144 1145 const sides = [ 1146 "top", 1147 "right", 1148 "bottom", 1149 "left" 1150 ]; 1151 1152 const entry = 1153 sides[ 1154 Math.floor( 1155 Math.random() * 1156 sides.length 1157 ) 1158 ]; 1159 1160 const opposite = { 1161 top: "bottom", 1162 right: "left", 1163 bottom: "top", 1164 left: "right" 1165 }; 1166 1167 const getOutsidePoint = (side) => 1168 { 1169 switch (side) 1170 { 1171 case "top": 1172 return { 1173 x: centre.x, 1174 y: -height 1175 }; 1176 1177 case "right": 1178 return { 1179 x: window.innerWidth + width, 1180 y: centre.y 1181 }; 1182 1183 case "bottom": 1184 return { 1185 x: centre.x, 1186 y: window.innerHeight + height 1187 }; 1188
1189 case "left": 1190 default: 1191 return { 1192 x: -width, 1193 y: centre.y 1194 }; 1195 } 1196 }; 1197 1198 const start = 1199 getOutsidePoint(entry); 1200 1201 const end = 1202 getOutsidePoint(opposite[entry]); 1203 1204 const frames = []; 1205 const STEPS = 60; 1206 const base = baseValues(instance); 1207 1208 const ARRIVE_END = 0.30; 1209 const SPIN_END = 0.70; 1210 1211 const spinDirection = 1212 Math.random() < 0.5 1213 ? 1 1214 : -1; 1215 1216 for (let i = 0; i <= STEPS; i++) 1217 { 1218 const t = i / STEPS; 1219 1220 let x; 1221 let y; 1222 let rotation; 1223 1224 if (t < ARRIVE_END) 1225 { 1226 const p = 1227 t / 1228 ARRIVE_END; 1229 1230 const movement = 1231 1 - 1232 Math.pow(1 - p, 4); 1233 1234 x = 1235 start.x + 1236 ((centre.x - start.x) * movement); 1237 1238 y = 1239 start.y + 1240 ((centre.y - start.y) * movement); 1241 1242 rotation = 1243 base.rotation; 1244 } 1245 else if (t < SPIN_END) 1246 { 1247 const p = 1248 (t - ARRIVE_END) / 1249 (SPIN_END - ARRIVE_END); 1250 1251 x = centre.x; 1252 y = centre.y; 1253 1254 rotation = 1255 base.rotation + 1256 ( 1257 p * 1258 720 * 1259 spinDirection 1260 ); 1261 } 1262 else 1263 { 1264 const p = 1265 (t - SPIN_END) / 1266 (1 - SPIN_END); 1267 1268 const movement = 1269 Math.pow(p, 3); 1270 1271 x = 1272 centre.x + 1273 ((end.x - centre.x) * movement); 1274 1275 y = 1276 centre.y + 1277 ((end.y - centre.y) * movement); 1278 1279 rotation = 1280 base.rotation + 1281 (720 * spinDirection); 1282 } 1283 1284 frames.push({ 1285 left: px(x), 1286 top: px(y), 1287 transform: transform(base.scale, rotation), 1288 opacity: base.opacity, 1289 offset: t 1290 }); 1291 } 1292 1293 return { 1294 frames, 1295 1296 options: { 1297 duration: instance.engine.duration, 1298 easing: "linear" 1299 } 1300 }; 1301}); 1302 1303StickerEngine.registerAnimation("cyclone", function(instance) 1304{ 1305 const centre = 1306 StickerEngine.resolvePosition( 1307 "centre", 1308 instance 1309 ); 1310 1311 const frames = []; 1312 const STEPS = 80; 1313 const base = baseValues(instance); 1314 1315 const viewportRadius = 1316 Math.hypot( 1317 window.innerWidth, 1318 window.innerHeight 1319 ) / 1320 2; 1321 1322 const stickerRadius = 1323 Math.max( 1324 instance.engine.displayWidth, 1325 instance.engine.displayHeight 1326 ); 1327 1328 const START_RADIUS = 1329 viewportRadius + 1330 stickerRadius + 1331 100; 1332 1333 const START_ANGLE = 1334 Math.random() * 1335 Math.PI * 1336 2; 1337 1338 const DIRECTION = 1339 Math.random() < 0.5 1340 ? 1 1341 : -1; 1342 1343 const ORBITS = 1344 3.5 + 1345 (Math.random() * 1.5); 1346 1347 const SPIRAL_END = 0.82; 1348 const POP_END = 0.88; 1349 const RECOIL_END = 0.94; 1350 1351 const POP_SCALE = 1352 base.scale * 1353 1.9; 1354 1355 const RECOIL_SCALE = 1356 base.scale * 1357 1.35; 1358 1359 const END_SCALE = 1360 base.scale * 1361 0.02; 1362 1363 for (let i = 0; i <= STEPS; i++) 1364 { 1365 const t = i / STEPS; 1366 1367 let x; 1368 let y; 1369 let scale; 1370 let opacity = base.opacity; 1371 1372 if (t < SPIRAL_END) 1373 { 1374 const p = 1375 t / 1376 SPIRAL_END; 1377 1378 const radius = 1379 START_RADIUS * 1380 Math.pow(1 - p, 1.25); 1381 1382 const angle = 1383 START_ANGLE + 1384 ( 1385 p * 1386 Math.PI * 1387 2 * 1388 ORBITS * 1389 DIRECTION 1390 ); 1391 1392 x = 1393 centre.x + 1394 (Math.cos(angle) * radius); 1395 1396 y = 1397 centre.y + 1398 (Math.sin(angle) * radius); 1399 1400 scale = 1401 base.scale * 1402 ( 1403 1 + 1404 ( 1405 Math.sin( 1406 p * 1407 Math.PI * 1408 ORBITS * 1409 2 1410 ) * 1411 0.025 * 1412 p 1413 ) 1414 ); 1415 } 1416 else if (t < POP_END) 1417 { 1418 const p = 1419 (t - SPIRAL_END) / 1420 (POP_END - SPIRAL_END); 1421 1422 const movement = 1423 1 - 1424 Math.pow(1 - p, 4); 1425 1426 x = centre.x; 1427 y = centre.y; 1428 1429 scale = 1430 base.scale + 1431 ( 1432 (POP_SCALE - base.scale) * 1433 movement 1434 ); 1435 } 1436 else if (t < RECOIL_END) 1437 { 1438 const p = 1439 (t - POP_END) / 1440 (RECOIL_END - POP_END); 1441 1442 const movement = 1443 p * 1444 p * 1445 (3 - (2 * p)); 1446 1447 x = centre.x; 1448 y = centre.y; 1449 1450 scale = 1451 POP_SCALE + 1452 ( 1453 (RECOIL_SCALE - POP_SCALE) * 1454 movement 1455 ); 1456 } 1457 else 1458 { 1459 const p = 1460 (t - RECOIL_END) / 1461 (1 - RECOIL_END); 1462 1463 x = centre.x; 1464 y = centre.y; 1465 1466 scale = 1467 RECOIL_SCALE + 1468 ( 1469 (END_SCALE - RECOIL_SCALE) * 1470 Math.pow(p, 1.8) 1471 ); 1472 1473 opacity = 1474 base.opacity * 1475 (1 - Math.pow(p, 1.2)); 1476 } 1477 1478 // Cyclone movement spirals, but the sticker image does not rotate. 1479 frames.push({ 1480 left: px(x), 1481 top: px(y), 1482 transform: transform(scale, base.rotation), 1483 opacity, 1484 offset: t 1485 }); 1486 } 1487 1488 return { 1489 frames, 1490 1491 options: { 1492 duration: instance.engine.duration, 1493 easing: "linear" 1494 } 1495 }; 1496});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.