apptify 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_LICENSES.txt +23 -0
- package/dist/apptify.css +1 -1
- package/dist/apptify.js +2395 -2451
- package/dist/components/AppleCard.vue.d.ts +1 -1
- package/dist/components/content.d.ts +81 -20
- package/dist/components/forms.d.ts +2 -2
- package/dist/components/foundation.d.ts +133 -11
- package/dist/components/mobile-image-viewer.d.ts +35 -12
- package/dist/components/navibar.d.ts +2 -2
- package/dist/components/overlays.d.ts +10 -59
- package/dist/components/scrollbar.d.ts +88 -0
- package/dist/components/speed-dial.d.ts +157 -0
- package/dist/core/context.d.ts +11 -0
- package/dist/core/device.d.ts +2 -0
- package/dist/core/image-gallery-motion.d.ts +2 -1
- package/dist/core/image-motion.d.ts +1 -1
- package/dist/core/layers.d.ts +1 -0
- package/dist/core/motion-driver.d.ts +40 -0
- package/dist/core/motion.d.ts +1 -1
- package/dist/core/ripple.d.ts +16 -0
- package/dist/core/scroll-motion.d.ts +6 -0
- package/dist/core/scrollbar.d.ts +14 -0
- package/dist/core/speed-dial.d.ts +21 -0
- package/dist/index.d.ts +237 -26
- package/docs/COMPONENTS.md +10 -6
- package/package.json +5 -7
- package/templates/playground/README.md +1 -1
- package/templates/playground/package.json +1 -2
- package/templates/playground/source-snapshot.json +299 -66
- package/templates/playground/src/App.vue +1 -0
- package/templates/playground/src/ComponentDemo.vue +30 -21
- package/templates/playground/src/catalog.ts +70 -65
- package/templates/playground/src/components/ComponentIndex.vue +10 -14
- package/templates/playground/src/components/MasonryFeed.vue +52 -0
- package/templates/playground/src/components/UserMenu.vue +1 -1
- package/templates/playground/src/components/forbidden-paper-scene.vue +367 -0
- package/templates/playground/src/components/forbidden-paper-story.ts +233 -0
- package/templates/playground/src/editor/ComponentPlayground.vue +89 -0
- package/templates/playground/src/editor/ConfigurableEditor.vue +1 -1
- package/templates/playground/src/editor/apple-input-source.ts +1 -1
- package/templates/playground/src/editor/component-metadata.ts +72 -0
- package/templates/playground/src/editor/component-playground.ts +162 -0
- package/templates/playground/src/editor/contract.ts +1 -1
- package/templates/playground/src/editor/documents.ts +11 -2
- package/templates/playground/src/editor/handbook.ts +22 -0
- package/templates/playground/src/editor/handbooks/apple-accordion.ts +66 -0
- package/templates/playground/src/editor/handbooks/apple-action-sheet.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-alert.ts +67 -0
- package/templates/playground/src/editor/handbooks/apple-aside.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-auto-size.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-autocomplete.ts +114 -0
- package/templates/playground/src/editor/handbooks/apple-avatar-group.ts +50 -0
- package/templates/playground/src/editor/handbooks/apple-avatar.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-back-top.ts +62 -0
- package/templates/playground/src/editor/handbooks/apple-badge.ts +62 -0
- package/templates/playground/src/editor/handbooks/apple-breadcrumbs.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-button.ts +92 -0
- package/templates/playground/src/editor/handbooks/apple-card.ts +112 -0
- package/templates/playground/src/editor/handbooks/apple-cascader.ts +102 -0
- package/templates/playground/src/editor/handbooks/apple-checkbox.ts +85 -0
- package/templates/playground/src/editor/handbooks/apple-color-picker.ts +79 -0
- package/templates/playground/src/editor/handbooks/apple-container.ts +26 -0
- package/templates/playground/src/editor/handbooks/apple-date-picker.ts +104 -0
- package/templates/playground/src/editor/handbooks/apple-dialog.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-divider.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-drawer.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-empty.ts +43 -0
- package/templates/playground/src/editor/handbooks/apple-floating-group.ts +51 -0
- package/templates/playground/src/editor/handbooks/apple-form-field.ts +69 -0
- package/templates/playground/src/editor/handbooks/apple-form.ts +76 -0
- package/templates/playground/src/editor/handbooks/apple-grid.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-image.ts +122 -0
- package/templates/playground/src/editor/handbooks/apple-infinite-scroll.ts +73 -0
- package/templates/playground/src/editor/handbooks/apple-link.ts +68 -0
- package/templates/playground/src/editor/handbooks/apple-list.ts +67 -0
- package/templates/playground/src/editor/handbooks/apple-menu.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-navibar.ts +100 -0
- package/templates/playground/src/editor/handbooks/apple-otp-input.ts +108 -0
- package/templates/playground/src/editor/handbooks/apple-overlay-host.ts +13 -0
- package/templates/playground/src/editor/handbooks/apple-pagination.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-popover.ts +112 -0
- package/templates/playground/src/editor/handbooks/apple-progress.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-provider.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-pull-refresh.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-radio-group.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-rate.ts +97 -0
- package/templates/playground/src/editor/handbooks/apple-scroll-bar.ts +15 -0
- package/templates/playground/src/editor/handbooks/apple-search.ts +49 -0
- package/templates/playground/src/editor/handbooks/apple-segmented-control.ts +85 -0
- package/templates/playground/src/editor/handbooks/apple-select.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-sheet.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-skeleton.ts +74 -0
- package/templates/playground/src/editor/handbooks/apple-slider.ts +103 -0
- package/templates/playground/src/editor/handbooks/apple-snackbar.ts +89 -0
- package/templates/playground/src/editor/handbooks/apple-speed-dial-item.ts +16 -0
- package/templates/playground/src/editor/handbooks/apple-speed-dial.ts +13 -0
- package/templates/playground/src/editor/handbooks/apple-spinner.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-stack.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-statistic.ts +69 -0
- package/templates/playground/src/editor/handbooks/apple-stepper.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-steps.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-switch.ts +79 -0
- package/templates/playground/src/editor/handbooks/apple-tab-bar.ts +66 -0
- package/templates/playground/src/editor/handbooks/apple-table.ts +176 -0
- package/templates/playground/src/editor/handbooks/apple-tabs.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-tag.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-textarea.ts +109 -0
- package/templates/playground/src/editor/handbooks/apple-timeline.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-tooltip.ts +45 -0
- package/templates/playground/src/editor/handbooks/apple-transition.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-tree.ts +84 -0
- package/templates/playground/src/editor/handbooks/apple-upload.ts +126 -0
- package/templates/playground/src/editor/playground-examples.ts +105 -0
- package/templates/playground/src/images.ts +3 -0
- package/templates/playground/src/router/index.ts +19 -2
- package/templates/playground/src/style.css +4 -4
- package/templates/playground/src/views/403.vue +65 -0
- package/templates/playground/src/views/component-docs/[component].vue +11 -14
- package/templates/playground/src/views/components.vue +11 -41
- package/templates/playground/src/views/index.vue +3 -1
- package/templates/playground/src/views/settings.vue +5 -2
- package/tooling/create-playground.mjs +2 -2
- package/tooling/prepare-playground.mjs +3 -3
- package/tooling/test-create-apptify.mjs +3 -1
- package/tooling/test-playground-package.mjs +10 -0
- package/dist/components/gallery-scrollbar.d.ts +0 -46
- package/dist/core/desktop-image-enter.d.ts +0 -6
- package/dist/core/desktop-image-return.d.ts +0 -1
- package/templates/playground/src/components/DirectoryDialog.vue +0 -20
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<figure ref="scene" class="meal-scene" :class="{ 'meal-scene--dark': dark }" :data-story="story" :data-playing="canAnimate && !suspended" :data-motion="motion">
|
|
3
|
+
<div class="meal-scene__window">
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" ref="art" class="meal-scene__art" viewBox="0 0 1000 660" role="img" :aria-labelledby="`${uid}-title ${uid}-description`">
|
|
5
|
+
<title :id="`${uid}-title`">小狗与够不到的鸡腿</title>
|
|
6
|
+
<desc :id="`${uid}-description`">饥饿的小狗坐在餐桌旁,肚子咕噜作响,镜头逐渐拉远。主人开始吃饭,小狗伸爪去够鸡腿,主人轻轻挡开它的爪子;小狗收回爪子,过一会儿又试一次。</desc>
|
|
7
|
+
<defs>
|
|
8
|
+
<linearGradient :id="`${uid}-wall`" x2=".3" y2="1"><stop stop-color="var(--dinner-wall-top)"></stop><stop offset="1" stop-color="var(--dinner-wall)"></stop></linearGradient>
|
|
9
|
+
<linearGradient :id="`${uid}-wood`" x2=".9" y2=".3"><stop stop-color="#d6b17e"></stop><stop offset="1" stop-color="#bc9166"></stop></linearGradient>
|
|
10
|
+
<linearGradient :id="`${uid}-chicken`" x1="0" y1="0" x2=".8" y2="1"><stop stop-color="#da9b51"></stop><stop offset=".55" stop-color="#c7813e"></stop><stop offset="1" stop-color="#a86035"></stop></linearGradient>
|
|
11
|
+
<filter :id="`${uid}-shadow`" x="-35%" y="-30%" width="180%" height="185%"><feDropShadow dx="1" dy="6" stdDeviation="3" flood-color="#39291e" flood-opacity=".13"></feDropShadow></filter>
|
|
12
|
+
<filter :id="`${uid}-soft-shadow`" x="-30%" y="-30%" width="170%" height="175%"><feDropShadow dx="1" dy="3" stdDeviation="2" flood-color="#39291e" flood-opacity=".12"></feDropShadow></filter>
|
|
13
|
+
<pattern :id="`${uid}-grain`" width="41" height="37" patternUnits="userSpaceOnUse"><path d="M4 6h1m15 11h1m13-9h.7M9 29h1m26 3h.8" stroke="#5e472e" stroke-opacity=".14" stroke-width="1"></path><path d="M8 11l3-1m13 16 3-1m-4-23 2-1" stroke="#fff8e5" stroke-opacity=".2" stroke-width=".7"></path></pattern>
|
|
14
|
+
<pattern :id="`${uid}-weave`" width="11" height="11" patternUnits="userSpaceOnUse"><path d="M0 4h11M4 0v11" fill="none" stroke="#f6ebd7" stroke-opacity=".16" stroke-width=".7"></path></pattern>
|
|
15
|
+
<clipPath :id="`${uid}-frame`"><path d="M36 74Q37 38 75 35L909 20Q957 19 963 68L986 565Q987 608 942 614L84 635Q29 635 27 585Z"></path></clipPath>
|
|
16
|
+
<clipPath :id="`${uid}-window`"><path d="m80 108 244-9 2 180-246 2Z"></path></clipPath>
|
|
17
|
+
<clipPath :id="`${uid}-photo-clip`" clipPathUnits="userSpaceOnUse"><path d="m452 117 97-3 1 85-96 4Z"></path></clipPath>
|
|
18
|
+
</defs>
|
|
19
|
+
<path d="M44 90Q43 52 83 49L915 33Q970 35 974 85L994 574Q996 620 945 628L86 647Q35 647 36 598Z" fill="var(--dinner-frame-shadow)"></path>
|
|
20
|
+
<g :clip-path="`url(#${uid}-frame)`">
|
|
21
|
+
<path d="M0 0H1000V660H0Z" :fill="`url(#${uid}-wall)`"></path>
|
|
22
|
+
<g data-part="camera" transform="translate(0 0) scale(1)">
|
|
23
|
+
|
|
24
|
+
<path d="m0 338 1000 6v328H0Z" fill="var(--dinner-panel)"></path>
|
|
25
|
+
<path d="M0 350 1000 358M105 348l-1 94m132-91 3 88m130-90 6 84m126-80 12 80m124-79 9 77m115-76 17 83m116-82 6 83" fill="none" stroke="var(--dinner-panel-line)" stroke-width="2" opacity=".45"></path>
|
|
26
|
+
<path d="m0 425 1000 11v245H0Z" fill="var(--dinner-floor)"></path>
|
|
27
|
+
<path d="M0 425 1000 436" fill="none" stroke="var(--dinner-baseboard)" stroke-width="10"></path>
|
|
28
|
+
<path d="m154 429-91 226m298-223-27 223m231-221 62 227m152-225 140 225M0 503l1000 10M0 603l1000 6" fill="none" stroke="var(--dinner-floor-line)" stroke-width="1.5" opacity=".4"></path>
|
|
29
|
+
<path d="m81 278 243-2 231 162-411-9Z" fill="var(--dinner-daylight)" opacity=".28"></path>
|
|
30
|
+
<g :filter="`url(#${uid}-shadow)`">
|
|
31
|
+
<path d="m69 97 268-11 2 210-272 3Z" fill="var(--dinner-window-frame)"></path>
|
|
32
|
+
<g :clip-path="`url(#${uid}-window)`">
|
|
33
|
+
<path d="M70 93H333V286H70Z" fill="var(--dinner-window-sky)"></path>
|
|
34
|
+
<circle cx="259" cy="150" r="29" fill="var(--dinner-sun)"></circle>
|
|
35
|
+
<path d="m60 231 76-61 70 42 52-26 89 46v62H60Z" fill="var(--dinner-hill-far)"></path>
|
|
36
|
+
<path d="m136 170 4 51 66-9Z" fill="var(--dinner-hill-fold)"></path>
|
|
37
|
+
<path d="M57 250q76-29 125-3t165-17v80H57Z" fill="var(--dinner-hill-near)"></path>
|
|
38
|
+
<path d="m92 276 23-38-9 1 18-34 19 32-8-1 25 39Z" fill="var(--dinner-tree)" opacity=".8"></path>
|
|
39
|
+
<path d="M202 102v181M80 194l246-5" stroke="var(--dinner-window-frame)" stroke-width="9"></path>
|
|
40
|
+
</g>
|
|
41
|
+
<path d="m62 296 282-5-7 13-273 5Z" fill="#b09a77"></path>
|
|
42
|
+
<path d="m64 288 274-6 6 9-282 5Z" fill="#ece0c2"></path>
|
|
43
|
+
</g>
|
|
44
|
+
|
|
45
|
+
<g data-part="wall-photo" :filter="`url(#${uid}-shadow)`">
|
|
46
|
+
<path d="m445 100 55-36 61 35" fill="none" stroke="#9b896a" stroke-width="1.6"></path>
|
|
47
|
+
<circle cx="500" cy="64" r="2.5" fill="#8c7c60"></circle>
|
|
48
|
+
<path d="m429 94 143-3 4 129-144 5Z" fill="#ad9570"></path>
|
|
49
|
+
<path d="m435 100 131-3 3 118-131 5Z" fill="#ceb994"></path>
|
|
50
|
+
<path d="m444 108 113-3 2 102-112 4Z" fill="#f0e4c9"></path>
|
|
51
|
+
<g data-part="photo-art" :clip-path="`url(#${uid}-photo-clip)`">
|
|
52
|
+
<path data-part="photo-sky" d="m452 117 97-3 1 85-96 4Z" fill="var(--dinner-photo-sky)"></path>
|
|
53
|
+
<g data-part="photo-night" class="dinner-photo-night" fill="#ece1b4">
|
|
54
|
+
<path d="M530 126A11 11 0 1 0 530 145A9 9 0 0 1 530 126Z"></path>
|
|
55
|
+
<circle cx="465" cy="130" r="1"></circle><circle cx="492" cy="125" r="1.2"></circle>
|
|
56
|
+
<circle cx="503" cy="143" r=".9"></circle><circle cx="540" cy="149" r="1"></circle>
|
|
57
|
+
</g>
|
|
58
|
+
<g data-part="photo-day" class="dinner-photo-day">
|
|
59
|
+
<circle cx="523" cy="135" r="10" fill="#f7e8ac"></circle>
|
|
60
|
+
<path d="M463 131h14m6-7h12" stroke="#f8f0dd" stroke-width="3" stroke-linecap="round" opacity=".7"></path>
|
|
61
|
+
</g>
|
|
62
|
+
<path d="M453 159Q477 145 504 158T550 152L550 176 453 177Z" fill="var(--dinner-photo-shore)"></path>
|
|
63
|
+
<path d="m453 171 97-7v35l-96 4Z" fill="var(--dinner-photo-lake)"></path>
|
|
64
|
+
<path d="m513 177 20-1m-25 8 18-1m-9 8 21-1" stroke="var(--dinner-photo-reflection)" stroke-width="1.4" opacity=".6"></path>
|
|
65
|
+
<path d="m454 175 26-8 24 11-26 9-24 2Z" fill="var(--dinner-photo-bank)"></path>
|
|
66
|
+
<path d="M473 158 480 152 490 157 491 171 472 172Z" fill="#b79973"></path>
|
|
67
|
+
<path d="m469 159 11-11 14 9-4 3-11-7-6 7Z" fill="#7c6658"></path>
|
|
68
|
+
<path d="m478 162 6-.5v6l-6 .5Z" fill="var(--dinner-photo-window)"></path>
|
|
69
|
+
<path d="M457.5 163H460.5V175H457.5Z" fill="#806044"></path>
|
|
70
|
+
<path d="M459 149 454 161 456 161 452 169 467 168 462 161 465 161Z" fill="var(--dinner-photo-tree)" stroke="var(--dinner-photo-tree)" stroke-width="1" stroke-linejoin="round"></path>
|
|
71
|
+
</g><path d="m454 108 103-3m-113 3 3 103" fill="none" stroke="#fff8e3" opacity=".4"></path>
|
|
72
|
+
</g>
|
|
73
|
+
|
|
74
|
+
<path d="m102 266 35-1-5 22-22 1Z" fill="#aa7958"></path><path d="m103 266 17 1 2 20-14 1Z" fill="#bd8e68"></path>
|
|
75
|
+
<path d="m119 269-3-52m2 35-12-18m12 4 15-15" fill="none" stroke="#75836a" stroke-width="2.4"></path>
|
|
76
|
+
<path d="M116 235q-19 0-22-17 20 0 22 17m3 9q0-19 20-23-2 18-20 23m-3-17q-10-8-3-23 11 9 3 23" fill="#899776"></path>
|
|
77
|
+
|
|
78
|
+
<g :filter="`url(#${uid}-shadow)`">
|
|
79
|
+
<path d="m747 81 151 4 17 316-177-2Z" fill="var(--dinner-chair-dark)"></path>
|
|
80
|
+
<path d="m760 92 125 4 12 292-147-3Z" fill="var(--dinner-chair)"></path>
|
|
81
|
+
<path d="m719 388 183 2 20 24-216-2Z" fill="#9e7b55"></path>
|
|
82
|
+
<path d="m720 409 23 1-9 147-15 2Zm165 1 21-1 20 142-16 4Z" fill="var(--dinner-chair-dark)"></path>
|
|
83
|
+
</g>
|
|
84
|
+
<g :filter="`url(#${uid}-shadow)`">
|
|
85
|
+
<path d="M728-56 806-57 808 42 776 72 728 44Z" fill="#d8a477"></path>
|
|
86
|
+
<path d="M781-50 806-57 808 42 776 72 766 55 779 25Z" fill="#bd845e"></path>
|
|
87
|
+
<path data-part="person-torso" d="M702 47 728 37 752 58 776 68 810 27 860 64Q889 78 888 110L864 198 878 297 689 305 671 204 668 153Q634 149 635 123Q637 73 702 47Z" fill="var(--dinner-shirt)"></path>
|
|
88
|
+
<path d="M702 47 728 37 752 58 722 87 692 58 672 66Z" fill="var(--dinner-shirt-light)"></path>
|
|
89
|
+
<path d="M810 27 776 68 805 86 832 52 860 64Z" fill="var(--dinner-shirt-light)"></path>
|
|
90
|
+
<path d="m752 58 24 10-6 231-15 2Z" fill="var(--dinner-shirt-dark)" opacity=".7"></path>
|
|
91
|
+
<path d="m848 115 18 48-4 68 16 66-53 2 8-86Z" fill="var(--dinner-shirt-dark)"></path>
|
|
92
|
+
|
|
93
|
+
<path d="m785 129 47 3-5 39-23 8-21-17Z" fill="var(--dinner-shirt-dark)" opacity=".38"></path>
|
|
94
|
+
<path d="m785 129 47 3m-90-20 4 57m-50 36 24 12" fill="none" stroke="var(--dinner-shirt-light)" stroke-width="2" opacity=".5"></path>
|
|
95
|
+
<g fill="#d5d5b9"><circle cx="764" cy="120" r="2.2"></circle><circle cx="762" cy="163" r="2.2"></circle><circle cx="761" cy="208" r="2.2"></circle></g>
|
|
96
|
+
</g>
|
|
97
|
+
|
|
98
|
+
<path d="m483 548 409-5 79 35-440 13Z" fill="#594531" opacity=".12"></path>
|
|
99
|
+
<g :filter="`url(#${uid}-shadow)`">
|
|
100
|
+
<path d="m499 356 35 3-15 223-18 2Z" fill="#95714f"></path><path d="m499 356 13 2-1 224-10 2Z" fill="#c59965"></path>
|
|
101
|
+
<path d="m889 355 33-5 23 229-18 4Z" fill="#8b684b"></path><path d="m910 355 12-5 23 229-10 1Z" fill="#bb925f"></path>
|
|
102
|
+
<path d="m457 325 553-4-5 56-542 9Z" fill="#a47d55"></path>
|
|
103
|
+
<path d="m463 343 542-6v40l-542 9Z" fill="#b58b5e"></path>
|
|
104
|
+
<path d="m480 280 503-4 38 49-569 14Z" :fill="`url(#${uid}-wood)`"></path>
|
|
105
|
+
<path d="m452 327 569-12v13l-569 15Z" fill="#dec08c"></path>
|
|
106
|
+
<path d="m500 294 176-4m-161 19 135-3m259-15 58-2m-124 26 136-3" fill="none" stroke="#8d6748" stroke-width="1.4" opacity=".23"></path>
|
|
107
|
+
</g>
|
|
108
|
+
<g data-part="tablecloth" :filter="`url(#${uid}-shadow)`">
|
|
109
|
+
<path data-part="cloth-silhouette" d="M636 281 931 277 958 324 958 566 899 581 805 575 695 585 659 575 652 330Z" fill="var(--dinner-cloth)"></path>
|
|
110
|
+
<path d="m652 330 22-1 8 226 14 30-37-10Z" fill="var(--dinner-cloth-fold)"></path>
|
|
111
|
+
<path d="m780 327 18-1 7 249-21 2Z" fill="var(--dinner-cloth-fold)" opacity=".3"></path>
|
|
112
|
+
<path d="m910 325 25-1 7 246-43 11Z" fill="var(--dinner-cloth-fold)" opacity=".52"></path>
|
|
113
|
+
<path d="M636 281 931 277 958 324 958 566 899 581 805 575 695 585 659 575 652 330Z" :fill="`url(#${uid}-weave)`"></path>
|
|
114
|
+
<path d="m669 562 27 9 109-10 94 6 48-13" fill="none" stroke="#e5e4c6" stroke-width="2" opacity=".65"></path>
|
|
115
|
+
<path d="m659 332 291-6" fill="none" stroke="#d9ddd0" stroke-width="1.3" opacity=".38"></path>
|
|
116
|
+
</g>
|
|
117
|
+
|
|
118
|
+
<ellipse cx="577" cy="313" rx="68" ry="10" fill="#6a5137" opacity=".15"></ellipse>
|
|
119
|
+
<g :filter="`url(#${uid}-soft-shadow)`">
|
|
120
|
+
<path d="M505 303q71-26 144-5l-8 17q-70 21-129 0Z" fill="#c9cbb7"></path>
|
|
121
|
+
<ellipse cx="576" cy="300" rx="72" ry="19" fill="#eee8cc"></ellipse>
|
|
122
|
+
<ellipse cx="577" cy="300" rx="56" ry="12" fill="#d8d8bd"></ellipse>
|
|
123
|
+
<path d="M533 303q40 12 82 0" fill="none" stroke="#fbf3d7" stroke-width="2"></path>
|
|
124
|
+
<path d="m587 303 20-22 4 9 18-12-7 17 12 2-24 15Z" fill="#84916a"></path>
|
|
125
|
+
<path d="m594 307 21-17m-8 10 12 2" stroke="#c8cd97" stroke-width="1"></path>
|
|
126
|
+
<g data-part="chicken">
|
|
127
|
+
<path d="m557 292-22 9-8-5-7 3-1 8 8 4 8-4 24-5Z" fill="#f1e3be"></path>
|
|
128
|
+
<path d="m534 303 23-8 2 7-24 5-8 4-6-3Z" fill="#d8c69f"></path>
|
|
129
|
+
<path d="M546 291q13-5 20-23 6-19 29-18l22 9 11 17-5 17-18 12-22 0-22-7-12 3Z" :fill="`url(#${uid}-chicken)`"></path>
|
|
130
|
+
<path d="M554 290q15-4 23-24 9-13 26-10l14 7-23-3-13 14-7 15-13 5Z" fill="#e7b267"></path>
|
|
131
|
+
<path d="m584 298 21 1 18-12-5 13-13 5-22 0-22-7-12 3-3-6 14-2Z" fill="#a96335" opacity=".6"></path>
|
|
132
|
+
<path d="m587 268 3-2m11 9 3-1m-19 10 3-2m25-7 2 1m-16 10 3-1" stroke="#885231" stroke-linecap="round" stroke-width="2.2" opacity=".5"></path>
|
|
133
|
+
</g>
|
|
134
|
+
<ellipse cx="789" cy="289" rx="55" ry="17" fill="#eee8cf"></ellipse><ellipse cx="790" cy="289" rx="43" ry="11" fill="#d1d2b7"></ellipse>
|
|
135
|
+
<path d="m766 290 8-12 14 2 4 9-12 5Z" fill="#b47f42"></path><g data-part="food-pile-back">
|
|
136
|
+
<path d="m751 285 5-12 14 1 6 11-13 9Z" fill="#aa7943"></path>
|
|
137
|
+
<path d="m756 273 14 1-5 5-12 3Z" fill="#cba466"></path>
|
|
138
|
+
<path d="m765 271 9-13 14 5 2 12-13 5Z" fill="#bc9350"></path>
|
|
139
|
+
<path d="m774 258 14 5-7 6-12-2Z" fill="#debd79"></path>
|
|
140
|
+
<path d="m786 271 3-14 13-5 10 9-7 16Z" fill="#c5a05b"></path>
|
|
141
|
+
<path d="m789 257 13-5 10 9-15 2Z" fill="#e2c581"></path>
|
|
142
|
+
<path d="m811 270 7-6 9 5-2 10-13 6-6-7Z" fill="#86956a"></path>
|
|
143
|
+
<path d="m808 280 13-6 8 8-3 10-16 2-6-6Z" fill="#a3ad78"></path>
|
|
144
|
+
<path d="m817 270 2 8m-7 6 9-2" fill="none" stroke="#c7ce95" stroke-width="1.3"></path>
|
|
145
|
+
</g>
|
|
146
|
+
<g data-part="water-cup">
|
|
147
|
+
<ellipse cx="952" cy="312" rx="18" ry="4" fill="#775c3e" opacity=".1"></ellipse>
|
|
148
|
+
<path d="m938 269 30-1-4 42q-12 7-23 0Z" fill="#d6e0cc" opacity=".78"></path>
|
|
149
|
+
<path d="m941 287 24-1-2 21q-10 5-20 0Z" fill="#94b3a8"></path>
|
|
150
|
+
<ellipse cx="953" cy="269" rx="15" ry="4.2" fill="#d7dfca" stroke="#edf0d9" stroke-width="1.8"></ellipse>
|
|
151
|
+
<path d="m943 277 1 21" fill="none" stroke="#f6ecd5" stroke-width="1.8" opacity=".8"></path>
|
|
152
|
+
</g>
|
|
153
|
+
</g>
|
|
154
|
+
|
|
155
|
+
<path d="m151 565 143-39 167 16 49 43-145 28-169-13Z" fill="var(--dinner-rug)"></path>
|
|
156
|
+
<path d="m173 566 124-29 151 16 40 27-128 23-153-13Z" fill="none" stroke="var(--dinner-rug-line)" stroke-width="2" opacity=".6"></path>
|
|
157
|
+
<path d="m170 570-22 4m25 3-22 4m28 4-20 5m302-16 21 7m-28 1 20 7m-32-3 19 8" stroke="var(--dinner-rug)" stroke-width="3"></path>
|
|
158
|
+
<ellipse data-part="dog-shadow" cx="342" cy="570" rx="82" ry="11" fill="#655036" opacity=".18" transform="translate(0 0)"></ellipse>
|
|
159
|
+
<g data-part="dog" transform="translate(354 567)">
|
|
160
|
+
<g data-part="tail" transform="rotate(0 -61 -75)"><path d="M-57-73C-91-57-126-80-132-113L-128-145-116-155-117-130C-111-106-90-101-64-109Z" fill="#bd8a55"></path><path d="M-117-130C-111-106-90-101-64-109L-57-96C-91-83-118-100-124-121Z" fill="#e2b783"></path></g>
|
|
161
|
+
<path data-part="dog-arm" d="M21.5938 -138.0749L115.8004 -150.964Q125.9094 -142.7904 124.6906 -155.7331L192.7762 -244.7126L177.2238 -257.2874L104.4725 -172.0803Q103.2537 -185.023 113.3627 -176.8494L18.4062 -171.9251Z" fill="#c99760"></path>
|
|
162
|
+
<path data-part="dog-arm-fold" d="M25 -155L118.5816 -163.9067L188 -252" fill="none" stroke="#e3b882" stroke-width="8" stroke-linecap="round"></path>
|
|
163
|
+
|
|
164
|
+
<g data-part="dog-paw" transform="translate(185 -251) rotate(-15)">
|
|
165
|
+
<path d="m-14-11 19-4 14 8 4 11-6 8-29-1-7-9Z" fill="#ecd0a0"></path><path d="m-12 5 28 0 7-1-6 8-29-1Z" fill="#d3aa75"></path>
|
|
166
|
+
<path d="m5-5 3 8m5-5 3 7" fill="none" stroke="#ab8158" stroke-width="1.4" stroke-linecap="round"></path>
|
|
167
|
+
</g>
|
|
168
|
+
<g :filter="`url(#${uid}-soft-shadow)`" data-part="dog-body">
|
|
169
|
+
<path d="M-58-110C-89-103-94-56-79-31L-61-8-6-5 5-15-15-32-15-86Z" fill="#b6804f"></path>
|
|
170
|
+
<path d="m-69-21 29-2 15 13 32 4 3 9-71 1-15-7Z" fill="#c89960"></path>
|
|
171
|
+
<path d="M-66-42C-87-86-61-129-40-155L-16-203 17-214 41-195 49-161 32-113 29-70 8-38-25-22Z" fill="#c99760"></path>
|
|
172
|
+
<path d="M-66-42C-83-88-58-128-37-150L-12-194-16-149-39-117-49-66-35-30Z" fill="#ddb17a"></path>
|
|
173
|
+
<path d="m17-214 24 19 8 34-17 48-3 43L12-43-6-62 8-112 0-151Z" fill="#eed4a0"></path>
|
|
174
|
+
<path d="m4-103 24 4 4 73 20 15 13 4 1 11-44 0-12-12-6-45Z" fill="#d9aa71" data-part="dog-planted-leg"></path>
|
|
175
|
+
<path d="m26-101 2 2 4 73 20 15 13 4 1 11-21 0-17-17Z" fill="#efd4a0" data-part="dog-planted-paw"></path>
|
|
176
|
+
<path d="m45-4 1 8m9-7 1 7" stroke="#a97e51" stroke-width="1.5"></path>
|
|
177
|
+
<g data-part="belly" transform="translate(-43 -78) scale(1 1) translate(43 78)">
|
|
178
|
+
<path d="M-62-105Q-78-75-61-47L-41-36-31-51Q-22-83-43-105Z" fill="#d2a46a"></path>
|
|
179
|
+
<path d="M-63-83q28-14 38 16l-5 22" fill="none" stroke="#ab7b4c" stroke-width="2" opacity=".6"></path>
|
|
180
|
+
</g>
|
|
181
|
+
</g>
|
|
182
|
+
<g data-part="dog-head" transform="rotate(-12 19 -196)">
|
|
183
|
+
<path d="m34-254 20-15 12 13-1 32-11 20-10-5Z" fill="#a96e45"></path>
|
|
184
|
+
<path d="m-7-215 6-33 20-22 30-4 21 13 6 18 29 9 12 21-24 12-30 2-20 21-31-2Z" fill="#d3a16a"></path>
|
|
185
|
+
<path d="m19-270 30-4 21 13 6 18-15-5-14-13-31 9-17 22 0-18Z" fill="#e6bb84"></path>
|
|
186
|
+
<path d="m61-235 16-8 28 9 12 21-24 12-30 2-20 21-18-2 24-23Z" fill="#f1d6a4"></path>
|
|
187
|
+
<path d="m86-218 26 2-10 14-20 8-23-4-7-7Z" fill="#e3bf8b"></path>
|
|
188
|
+
<path d="m103-235 15 7 2 9-11 4-10-8Z" fill="#514337"></path>
|
|
189
|
+
<path d="m106-232 8 3" stroke="#89705a" stroke-width="2" stroke-linecap="round"></path>
|
|
190
|
+
<path d="M72 -205 Q84 -212 99 -207" fill="none" stroke="#8d6645" stroke-width="2" stroke-linecap="round" data-part="dog-mouth"></path>
|
|
191
|
+
<g data-part="tongue" opacity="0"><path d="m83-202 12-2 1 13-5 6-7-4Z" fill="#c78f79"></path><path d="m90-200 1 9" stroke="#a77064" stroke-width="1"></path></g>
|
|
192
|
+
<path d="m4-192 19 8 19-1 11-10 0 11-14 11-18 1-20-10Z" fill="#83928b"></path>
|
|
193
|
+
<path d="m20-178 9 4-2 12-10-1-3-9Z" fill="#d1af64"></path><circle cx="21" cy="-169" r="2" fill="#f1d98f"></circle>
|
|
194
|
+
<g data-part="dog-eye" transform="translate(58 -241) scale(1 1) translate(-58 241)"><ellipse cx="58" cy="-241" rx="5.3" ry="6.5" fill="#493d32"></ellipse><circle data-part="pupil-shine" cx="60" cy="-243" r="1.8" fill="#faf0d3"></circle></g>
|
|
195
|
+
<path data-part="brow" d="M51 -251 Q57 -257 65 -255" fill="none" stroke="#a77649" stroke-width="2.3" stroke-linecap="round" transform="rotate(0.6803 58 -254)"></path>
|
|
196
|
+
<g data-part="ear" transform="rotate(0 7 -245)"><path d="m-4-246 22-13 8 19-6 35-11 24-20-10-8-27Z" fill="#93613f"></path><path d="m-4-246 13-6 1 31-5 26-12 2-9-20Z" fill="#b17b4f"></path><path d="m14-248 4 8-6 36-6 10" fill="none" stroke="#d09a64" stroke-width="2" opacity=".6"></path></g>
|
|
197
|
+
</g>
|
|
198
|
+
<g data-part="hunger" opacity="0" fill="none" stroke="#947649" stroke-width="2.5" stroke-linecap="round" transform="translate(0 0)"><path d="m-100-88-9 6 11 6-9 6m106-36 9 6-9 6 8 6"></path></g>
|
|
199
|
+
</g>
|
|
200
|
+
<ellipse cx="134" cy="584" rx="47" ry="7" fill="#715b40" opacity=".13"></ellipse>
|
|
201
|
+
<g :filter="`url(#${uid}-soft-shadow)`">
|
|
202
|
+
<path d="m95 558 73 0 11 24q-41 13-92 0Z" fill="var(--dinner-bowl)"></path>
|
|
203
|
+
<ellipse cx="132" cy="557" rx="37" ry="10" fill="#ddd9bb"></ellipse><ellipse cx="132" cy="558" rx="28" ry="6" fill="#a1a88d"></ellipse>
|
|
204
|
+
<path d="m94 576 81 0 4 6q-41 13-92 0Z" fill="#80988f"></path>
|
|
205
|
+
<path d="m123 569 15 0m-15-2v4m15-4v4" stroke="#e1dfc1" stroke-width="3" stroke-linecap="round"></path>
|
|
206
|
+
</g>
|
|
207
|
+
|
|
208
|
+
<g data-part="eat-arm-group">
|
|
209
|
+
<path data-part="eat-upper" fill="#d1a176" d="M839.8277 127.7053L906.1361 220.5234Q932.4698 225.8656 937.812 199.532L878.1723 102.2947A23 23 0 0 0 839.8277 127.7053Z"></path>
|
|
210
|
+
<path data-part="eat-sleeve" fill="var(--dinner-shirt)" d="M834.8263 131.0197L877.2045 187.7273Q911.854 194.7566 918.8833 160.1071L883.1737 98.9803A29 29 0 0 0 834.8263 131.0197Z"></path>
|
|
211
|
+
<path data-part="eat-cuff" fill="var(--dinner-shirt-light)" d="M874.3521 184.1471L880.983 193.4289L922.6618 165.8087L916.6978 156.085Z"></path>
|
|
212
|
+
<path data-part="eat-forearm" fill="#dfae80" d="M920.549 191.0812L780.5087 209.1358Q769.9037 221.4659 782.2338 232.071L923.3992 228.9742A19 19 0 0 0 920.549 191.0812Z"></path>
|
|
213
|
+
<g data-part="eat-hand" transform="translate(781.3713 220.6034) rotate(172)">
|
|
214
|
+
<path d="M0 6v-57m-8-11v10q8 7 16 0v-10m-11 0v10m6-10v10" fill="none" stroke="#a2aea2" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
215
|
+
<g data-part="fork-bite" opacity="1">
|
|
216
|
+
<path d="m-7-58 3-10 9 2 3 9-8 3Z" fill="#c59d55"></path>
|
|
217
|
+
<path d="m-4-68 9 2-5 5-7 3Z" fill="#e0bf78"></path>
|
|
218
|
+
</g><path d="M-12-9Q-5-15 6-11L13-4 12 11 5 17-7 15-13 7Z" fill="#dfae80"></path>
|
|
219
|
+
<path d="M-7-6Q-1-8 5-6M-8 0Q-2-2 4 0M-7 6Q-1 4 5 6" fill="none" stroke="#bd895f" stroke-width="1.2" stroke-linecap="round"></path>
|
|
220
|
+
</g>
|
|
221
|
+
</g>
|
|
222
|
+
<g data-part="food-pile-front">
|
|
223
|
+
<path d="m766 285 10-10 13 7-3 13-15 1-10-6Z" fill="#b28547"></path>
|
|
224
|
+
<path d="m776 275 13 7-11 2-12 1Z" fill="#d9b674"></path>
|
|
225
|
+
<path data-part="food-cover" d="M777 280 783 262 795 259 807 273 809 288 798 298 781 296 773 289Z" fill="#bd9753"></path>
|
|
226
|
+
<path d="m783 262 12-3 12 14-17 4-13 3Z" fill="#d9b975"></path>
|
|
227
|
+
<path d="m790 277 17-4 2 15-11 10-1-13Z" fill="#ac8347"></path>
|
|
228
|
+
<path d="m799 294 6-11 11 1 6 10-10 5Z" fill="#cba45f"></path>
|
|
229
|
+
<path d="m805 283 11 1-5 5-9 2Z" fill="#e4c680"></path>
|
|
230
|
+
<path d="m755 290 7-6 9 4 1 7-12 2Z" fill="#9aa575"></path>
|
|
231
|
+
<path d="m809 273 5-7 6 5-3 9Z" fill="#9dab77"></path>
|
|
232
|
+
</g>
|
|
233
|
+
<g data-part="deny-arm-group" transform="translate(668 98)">
|
|
234
|
+
<g data-part="deny-upper-group" transform="rotate(40.6782)">
|
|
235
|
+
<path data-part="deny-skin" d="M-25 65H25L20 135A20 20 0 0 1-20 135Z" fill="#dba879"></path>
|
|
236
|
+
<path data-part="deny-sleeve" d="M-29 1A30 30 0 0 1 30 1L32 72 26 87-25 87-31 72Z" fill="var(--dinner-shirt)"></path>
|
|
237
|
+
<path d="m-26 78 53 0-1 9-51 0Z" fill="var(--dinner-shirt-light)" opacity=".65"></path>
|
|
238
|
+
<g data-part="deny-forearm-group" transform="translate(0 135) rotate(-12.1468)">
|
|
239
|
+
<path data-part="deny-forearm" d="M-20 0A20 20 0 0 1 20 0L12 100Q0 113-12 100Z" fill="#dba879"></path>
|
|
240
|
+
<g data-part="deny-wrist" transform="translate(0 100)">
|
|
241
|
+
<path d="M10-7Q18-11 19-4L20 8Q19 13 13 13L7 4Z" fill="#dba879"></path>
|
|
242
|
+
<path d="M-11-12Q0-16 9-10L16 3 17 15Q17 22 12 24L7 24Q3 29-1 25Q-7 28-10 22L-14 13-14-2Z" fill="#dba879"></path>
|
|
243
|
+
<path d="m-7 7 2 13m5-14 2 16m5-15 2 12" fill="none" stroke="#b7825e" stroke-width="1.25" stroke-linecap="round"></path>
|
|
244
|
+
<g data-part="deny-hand" transform="translate(0 20)"></g>
|
|
245
|
+
</g>
|
|
246
|
+
</g>
|
|
247
|
+
</g>
|
|
248
|
+
</g>
|
|
249
|
+
<g data-part="tap" opacity="0" fill="none" stroke="#8e6f46" stroke-width="2.5" stroke-linecap="round"><path d="m522 300-9-5m39 26 9 3m-13-27 7-8"></path></g>
|
|
250
|
+
<path d="M-80-80H1100V760H-80Z" :fill="`url(#${uid}-grain)`" pointer-events="none"></path>
|
|
251
|
+
</g>
|
|
252
|
+
<path d="M40 78 43 581Q44 614 80 613" fill="none" stroke="#fff8de" stroke-width="2" opacity=".35"></path>
|
|
253
|
+
</g>
|
|
254
|
+
<g class="meal-scene__edition" fill="var(--apple-secondary, #74766e)" font-size="10" letter-spacing="2"><text x="44" y="20">NOT YOURS</text><text x="942" y="650" text-anchor="end">PAPER TALES — 02</text></g>
|
|
255
|
+
</svg>
|
|
256
|
+
</div>
|
|
257
|
+
<button v-if="canAnimate" class="meal-scene__pause" type="button" :aria-label="userPaused ? '继续动画' : '暂停动画'" :aria-pressed="userPaused" @click="userPaused = !userPaused">
|
|
258
|
+
<Play v-if="userPaused" :size="12" aria-hidden="true" /><Pause v-else :size="12" aria-hidden="true" />
|
|
259
|
+
</button>
|
|
260
|
+
</figure>
|
|
261
|
+
</template>
|
|
262
|
+
|
|
263
|
+
<script setup lang="ts">
|
|
264
|
+
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'
|
|
265
|
+
import { Pause, Play } from 'lucide-vue-next'
|
|
266
|
+
import { useApple } from 'apptify'
|
|
267
|
+
import { resolveMotion } from 'apptify'
|
|
268
|
+
import { createForbiddenPaperStory, createForbiddenRenderer, type ForbiddenPaperStory } from './forbidden-paper-story'
|
|
269
|
+
|
|
270
|
+
const uid = `meal-${useId().replace(/:/g, '')}`
|
|
271
|
+
const apple = useApple()
|
|
272
|
+
const scene = ref<HTMLElement>()
|
|
273
|
+
const art = ref<SVGSVGElement>()
|
|
274
|
+
const media = typeof window === 'undefined' ? undefined : window.matchMedia('(prefers-reduced-motion: reduce)')
|
|
275
|
+
const systemReduced = ref(media?.matches ?? false)
|
|
276
|
+
const motion = computed(() => resolveMotion('inherit', apple.motion.value.mode, apple.motion.value.reduced || systemReduced.value))
|
|
277
|
+
const canAnimate = computed(() => motion.value === 'full')
|
|
278
|
+
const dark = computed(() => apple.theme.value.current.scheme === 'dark')
|
|
279
|
+
const userPaused = ref(false)
|
|
280
|
+
const hidden = ref(typeof document !== 'undefined' && document.hidden)
|
|
281
|
+
const offscreen = ref(false)
|
|
282
|
+
const suspended = computed(() => userPaused.value || hidden.value || offscreen.value)
|
|
283
|
+
const story = ref<'intro' | 'loop' | 'static'>('static')
|
|
284
|
+
let player: ForbiddenPaperStory | undefined
|
|
285
|
+
let observer: IntersectionObserver | undefined
|
|
286
|
+
let mounted = false
|
|
287
|
+
|
|
288
|
+
function stop() { player?.dispose(); player = undefined }
|
|
289
|
+
function syncPlayback() { player?.setPaused(suspended.value) }
|
|
290
|
+
function play(intro: boolean) {
|
|
291
|
+
stop()
|
|
292
|
+
if (!mounted || !art.value) return
|
|
293
|
+
if (!canAnimate.value) { createForbiddenRenderer(art.value).still(); story.value = 'static'; return }
|
|
294
|
+
story.value = intro ? 'intro' : 'loop'
|
|
295
|
+
player = createForbiddenPaperStory(art.value, intro, () => { story.value = 'loop' })
|
|
296
|
+
syncPlayback()
|
|
297
|
+
}
|
|
298
|
+
function visibilityChanged() { hidden.value = document.hidden }
|
|
299
|
+
function mediaChanged() { systemReduced.value = media?.matches ?? false }
|
|
300
|
+
watch(suspended, syncPlayback, { flush: 'sync' })
|
|
301
|
+
watch(canAnimate, enabled => {
|
|
302
|
+
if (!mounted) return
|
|
303
|
+
if (enabled) play(false)
|
|
304
|
+
else play(false)
|
|
305
|
+
}, { flush: 'post' })
|
|
306
|
+
onMounted(async () => {
|
|
307
|
+
mounted = true
|
|
308
|
+
document.addEventListener('visibilitychange', visibilityChanged)
|
|
309
|
+
media?.addEventListener('change', mediaChanged)
|
|
310
|
+
if (typeof IntersectionObserver !== 'undefined' && scene.value) {
|
|
311
|
+
observer = new IntersectionObserver(([entry]) => { offscreen.value = !entry.isIntersecting }, { threshold: 0 })
|
|
312
|
+
observer.observe(scene.value)
|
|
313
|
+
}
|
|
314
|
+
await nextTick()
|
|
315
|
+
if (mounted) play(true)
|
|
316
|
+
})
|
|
317
|
+
onBeforeUnmount(() => {
|
|
318
|
+
mounted = false
|
|
319
|
+
stop()
|
|
320
|
+
observer?.disconnect()
|
|
321
|
+
media?.removeEventListener('change', mediaChanged)
|
|
322
|
+
document.removeEventListener('visibilitychange', visibilityChanged)
|
|
323
|
+
})
|
|
324
|
+
</script>
|
|
325
|
+
|
|
326
|
+
<style scoped>
|
|
327
|
+
.meal-scene {
|
|
328
|
+
--dinner-wall-top: #f4ead8; --dinner-wall: #e8e3c9; --dinner-frame-shadow: #d4cbbb;
|
|
329
|
+
--dinner-panel: #d6d8ba; --dinner-panel-line: #aeb795; --dinner-baseboard: #b8b69a;
|
|
330
|
+
--dinner-floor: #d4c39f; --dinner-floor-line: #b29a77; --dinner-daylight: #fff5ce;
|
|
331
|
+
--dinner-window-frame: #e4d7b6; --dinner-window-sky: #dde1c9; --dinner-sun: #fdf6d5;
|
|
332
|
+
--dinner-hill-far: #bbc6ad; --dinner-hill-fold: #d0d3b5; --dinner-hill-near: #9bad88; --dinner-tree: #829678;
|
|
333
|
+
--dinner-chair: #b29a70; --dinner-chair-dark: #917c58;
|
|
334
|
+
--dinner-shirt: #829585; --dinner-shirt-light: #a3af96; --dinner-shirt-dark: #627b6d;
|
|
335
|
+
--dinner-cloth: #aac4ba; --dinner-cloth-fold: #8daaa2;
|
|
336
|
+
--dinner-rug: #b4bb92; --dinner-rug-line: #d0ceaa; --dinner-bowl: #a2b6a7;
|
|
337
|
+
--dinner-photo-sky: #2b4051; --dinner-photo-shore: #465e65; --dinner-photo-lake: #567680;
|
|
338
|
+
--dinner-photo-bank: #3e5752; --dinner-photo-tree: #304d49; --dinner-photo-reflection: #d5d0ae; --dinner-photo-window: #f1d791;
|
|
339
|
+
position: relative; min-width: 0; margin: 0;
|
|
340
|
+
}
|
|
341
|
+
.meal-scene--dark {
|
|
342
|
+
--dinner-wall-top: #253536; --dinner-wall: #354a47; --dinner-frame-shadow: #121d1f;
|
|
343
|
+
--dinner-panel: #465b4e; --dinner-panel-line: #62765a; --dinner-baseboard: #3c5044;
|
|
344
|
+
--dinner-floor: #838366; --dinner-floor-line: #62634b; --dinner-daylight: #bdb37d;
|
|
345
|
+
--dinner-window-frame: #708170; --dinner-window-sky: #364c4e; --dinner-sun: #ddcd9d;
|
|
346
|
+
--dinner-hill-far: #466357; --dinner-hill-fold: #58715c; --dinner-hill-near: #334e42; --dinner-tree: #253e37;
|
|
347
|
+
--dinner-chair: #8a805b; --dinner-chair-dark: #696548;
|
|
348
|
+
--dinner-shirt: #667f71; --dinner-shirt-light: #8b9d82; --dinner-shirt-dark: #486759;
|
|
349
|
+
--dinner-cloth: #74978e; --dinner-cloth-fold: #5a7d76;
|
|
350
|
+
--dinner-rug: #657856; --dinner-rug-line: #899474; --dinner-bowl: #819c8e;
|
|
351
|
+
--dinner-photo-sky: #dce5d5; --dinner-photo-shore: #a7ba9a; --dinner-photo-lake: #98bcc0;
|
|
352
|
+
--dinner-photo-bank: #92a77a; --dinner-photo-tree: #718a63; --dinner-photo-reflection: #f8edcb; --dinner-photo-window: #6d847a;
|
|
353
|
+
}
|
|
354
|
+
.meal-scene__window { overflow: hidden; padding: 4px; }
|
|
355
|
+
.meal-scene__art { display: block; width: 100%; height: auto; overflow: visible; }
|
|
356
|
+
.meal-scene__art [data-part] { transform-box: view-box; }
|
|
357
|
+
.meal-scene .dinner-photo-day { display: none; }
|
|
358
|
+
.meal-scene .dinner-photo-night { display: inline; }
|
|
359
|
+
.meal-scene--dark .dinner-photo-day { display: inline; }
|
|
360
|
+
.meal-scene--dark .dinner-photo-night { display: none; }
|
|
361
|
+
|
|
362
|
+
.meal-scene__pause { position: absolute; right: 1.5%; bottom: -5px; width: 32px; height: 32px; padding: 0; display: grid; place-items: center; border: 0; border-radius: 50%; background: transparent; color: var(--apple-secondary); cursor: pointer; }
|
|
363
|
+
.meal-scene__pause:hover { background: var(--apple-hover); }
|
|
364
|
+
.meal-scene__pause:focus-visible { outline: 2px solid var(--apple-accent); outline-offset: 2px; }
|
|
365
|
+
@media (max-width: 880px) { .meal-scene__window { margin-inline: -12px; } .meal-scene__pause { right: -1%; } }
|
|
366
|
+
@media (max-width: 420px) { .meal-scene__edition { font-size: 12px; } .meal-scene__pause { bottom: -8px; } }
|
|
367
|
+
</style>
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
type Point = { x: number; y: number }
|
|
2
|
+
|
|
3
|
+
const INTRO_DURATION = 5400;
|
|
4
|
+
const LOOP_DURATION = 6400;
|
|
5
|
+
const CONTACT_TIME = 3000;
|
|
6
|
+
const clamp = (v: number, lo = 0, hi = 1) => Math.min(hi, Math.max(lo, v));
|
|
7
|
+
const mix = (a: number, b: number, t: number) => a + (b - a) * t;
|
|
8
|
+
const smooth = (t: number) => t * t * (3 - 2 * t);
|
|
9
|
+
const smoother = (t: number) => t * t * t * (t * (t * 6 - 15) + 10);
|
|
10
|
+
const n = (v: number) => Number(v.toFixed(4));
|
|
11
|
+
const xy = (p: Point) => `${n(p.x)} ${n(p.y)}`;
|
|
12
|
+
const add = (a: Point, b: Point, amount = 1) => ({ x: a.x + b.x * amount, y: a.y + b.y * amount });
|
|
13
|
+
const between = (a: Point, b: Point, t: number) => ({ x: mix(a.x, b.x, t), y: mix(a.y, b.y, t) });
|
|
14
|
+
function track(keys: Array<[number, number]>, t: number) {
|
|
15
|
+
if (t <= keys[0][0])
|
|
16
|
+
return keys[0][1];
|
|
17
|
+
for (let i = 1; i < keys.length; i++) {
|
|
18
|
+
if (t <= keys[i][0]) {
|
|
19
|
+
const [a, v] = keys[i - 1], [b, w] = keys[i];
|
|
20
|
+
return mix(v, w, smooth(clamp((t - a) / (b - a))));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return keys[keys.length - 1][1];
|
|
24
|
+
}
|
|
25
|
+
function pointTrack(keys: Array<[number, number, number]>, t: number) {
|
|
26
|
+
return { x: track(keys.map(k => [k[0], k[1]]), t), y: track(keys.map(k => [k[0], k[2]]), t) };
|
|
27
|
+
}
|
|
28
|
+
function rotate(p: Point, angle: number) {
|
|
29
|
+
const a = angle * Math.PI / 180;
|
|
30
|
+
return { x: p.x * Math.cos(a) - p.y * Math.sin(a), y: p.x * Math.sin(a) + p.y * Math.cos(a) };
|
|
31
|
+
}
|
|
32
|
+
function curve(a: Point, control: Point, b: Point, t: number) {
|
|
33
|
+
return between(between(a, control, t), between(control, b, t), t);
|
|
34
|
+
}
|
|
35
|
+
function normal(a: Point, b: Point) {
|
|
36
|
+
const d = Math.hypot(b.x - a.x, b.y - a.y) || 1;
|
|
37
|
+
return { x: -(b.y - a.y) / d, y: (b.x - a.x) / d };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function capsule(a: Point, b: Point, wa: number, wb: number) {
|
|
41
|
+
const u = normal(a, b), v = { x: u.y, y: -u.x };
|
|
42
|
+
return `M${xy(add(a, u, wa))}L${xy(add(b, u, wb))}Q${xy(add(b, v, wb))} ${xy(add(b, u, -wb))}L${xy(add(a, u, -wa))}A${wa} ${wa} 0 0 0 ${xy(add(a, u, wa))}Z`;
|
|
43
|
+
}
|
|
44
|
+
function strip(a: Point, b: Point, wa: number, wb: number) {
|
|
45
|
+
const u = normal(a, b);
|
|
46
|
+
return `M${xy(add(a, u, wa))}L${xy(add(b, u, wb))}L${xy(add(b, u, -wb))}L${xy(add(a, u, -wa))}Z`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function elbow(a: Point, b: Point, upper: number, lower: number, bend: number) {
|
|
50
|
+
const raw = Math.hypot(b.x - a.x, b.y - a.y);
|
|
51
|
+
const d = clamp(raw, Math.abs(upper - lower) + .001, upper + lower - .001);
|
|
52
|
+
const direction = { x: (b.x - a.x) / (raw || 1), y: (b.y - a.y) / (raw || 1) };
|
|
53
|
+
const along = (upper * upper - lower * lower + d * d) / (2 * d);
|
|
54
|
+
const height = Math.sqrt(Math.max(0, upper * upper - along * along));
|
|
55
|
+
return { x: a.x + direction.x * along - direction.y * height * bend, y: a.y + direction.y * along + direction.x * height * bend };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function bentLimb(a: Point, joint: Point, b: Point, wa: number, wj: number, wb: number) {
|
|
59
|
+
const u = normal(a, joint), v = normal(joint, b);
|
|
60
|
+
const corner = add(add(joint, u, wj), v, wj);
|
|
61
|
+
const inner = add(add(joint, u, -wj), v, -wj);
|
|
62
|
+
return `M${xy(add(a, u, wa))}L${xy(add(joint, u, wj))}Q${xy(corner)} ${xy(add(joint, v, wj))}L${xy(add(b, v, wb))}L${xy(add(b, v, -wb))}L${xy(add(joint, v, -wj))}Q${xy(inner)} ${xy(add(joint, u, -wj))}L${xy(add(a, u, -wa))}Z`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createForbiddenRenderer(art: SVGSVGElement) {
|
|
66
|
+
const parts = new Map<string, SVGElement>();
|
|
67
|
+
art.querySelectorAll<SVGElement>('[data-part]').forEach(el => parts.set(el.dataset.part!, el));
|
|
68
|
+
const part = (name: string) => {
|
|
69
|
+
const el = parts.get(name);
|
|
70
|
+
if (!el)
|
|
71
|
+
throw new Error(`Forbidden scene is missing data-part="${name}"`);
|
|
72
|
+
return el;
|
|
73
|
+
};
|
|
74
|
+
const attr = (name: string, key: string, value: string | number) => part(name).setAttribute(key, String(value));
|
|
75
|
+
const transform = (name: string, value: string) => attr(name, 'transform', value);
|
|
76
|
+
const opacity = (name: string, value: number) => attr(name, 'opacity', n(clamp(value)));
|
|
77
|
+
const frame = art.closest<HTMLElement>('.meal-scene');
|
|
78
|
+
function eatingArm(p: number, resting: boolean) {
|
|
79
|
+
|
|
80
|
+
const plate = { x: 790, y: 282 };
|
|
81
|
+
const mouth = { x: 777, y: 2 };
|
|
82
|
+
let tip = plate, angle = 172;
|
|
83
|
+
|
|
84
|
+
const bite = resting || p < 1.96 || p >= 4.05;
|
|
85
|
+
if (!resting) {
|
|
86
|
+
if (p >= .35 && p < 1.7) {
|
|
87
|
+
const u = smoother(clamp((p - .35) / 1.35));
|
|
88
|
+
tip = curve(plate, { x: 690, y: 155 }, mouth, u);
|
|
89
|
+
angle = mix(172, 335, u);
|
|
90
|
+
}
|
|
91
|
+
else if (p >= 1.7 && p < 2.3) {
|
|
92
|
+
tip = mouth;
|
|
93
|
+
angle = 335;
|
|
94
|
+
}
|
|
95
|
+
else if (p >= 2.3 && p < 3.8) {
|
|
96
|
+
const u = smoother(clamp((p - 2.3) / 1.5));
|
|
97
|
+
tip = curve(mouth, { x: 690, y: 155 }, plate, u);
|
|
98
|
+
angle = mix(335, 172, u);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const offset = rotate({ x: 0, y: -62 }, angle);
|
|
102
|
+
const wrist = { x: tip.x - offset.x, y: tip.y - offset.y };
|
|
103
|
+
const shoulder = { x: 859, y: 115 };
|
|
104
|
+
const joint = elbow(shoulder, wrist, 114, 141, -1);
|
|
105
|
+
attr('eat-upper', 'd', capsule(shoulder, joint, 23, 19));
|
|
106
|
+
attr('eat-sleeve', 'd', capsule(shoulder, between(shoulder, joint, .62), 29, 25));
|
|
107
|
+
attr('eat-cuff', 'd', strip(between(shoulder, joint, .58), between(shoulder, joint, .68), 25.4, 25));
|
|
108
|
+
|
|
109
|
+
attr('eat-forearm', 'd', capsule(joint, wrist, 19, 11.5));
|
|
110
|
+
transform('eat-hand', `translate(${xy(wrist)}) rotate(${n(angle)})`);
|
|
111
|
+
opacity('fork-bite', bite ? 1 : 0);
|
|
112
|
+
}
|
|
113
|
+
function blockingArm(p: number, resting: boolean) {
|
|
114
|
+
|
|
115
|
+
const upper = 135, forearm = 100, handReach = 20;
|
|
116
|
+
const distal = forearm + handReach;
|
|
117
|
+
const shoulder = { x: 668, y: 98 };
|
|
118
|
+
const target = add({ x: 539, y: 316 }, rotate({ x: 0, y: -10 }, -15));
|
|
119
|
+
const dx = target.x - shoulder.x, dy = target.y - shoulder.y;
|
|
120
|
+
const contactBend = Math.acos(clamp((dx * dx + dy * dy - upper * upper - distal * distal) / (2 * upper * distal), -1, 1));
|
|
121
|
+
const contactShoulder = (Math.atan2(-dx, dy) + Math.atan2(distal * Math.sin(contactBend), upper + distal * Math.cos(contactBend))) * 180 / Math.PI;
|
|
122
|
+
const extendedBend = contactBend * 180 / Math.PI;
|
|
123
|
+
let shoulderAngle = 5, flexion = 56;
|
|
124
|
+
if (!resting) {
|
|
125
|
+
if (p >= 2 && p < 2.76) {
|
|
126
|
+
const u = smoother((p - 2) / .76);
|
|
127
|
+
shoulderAngle = mix(5, contactShoulder + 15, u);
|
|
128
|
+
flexion = mix(56, 40, u);
|
|
129
|
+
}
|
|
130
|
+
else if (p >= 2.76 && p < 2.82) {
|
|
131
|
+
shoulderAngle = contactShoulder + 15;
|
|
132
|
+
flexion = 40;
|
|
133
|
+
}
|
|
134
|
+
else if (p >= 2.82 && p <= 3.18) {
|
|
135
|
+
|
|
136
|
+
shoulderAngle = mix(contactShoulder + 15, contactShoulder - 15, smooth((p - 2.82) / .36));
|
|
137
|
+
flexion = p <= 3
|
|
138
|
+
? mix(40, extendedBend, smooth(clamp((p - 2.82) / .18)))
|
|
139
|
+
: mix(extendedBend, 25, smooth(clamp((p - 3) / .18)));
|
|
140
|
+
}
|
|
141
|
+
else if (p > 3.18 && p < 4.25) {
|
|
142
|
+
const u = smoother((p - 3.18) / 1.07);
|
|
143
|
+
shoulderAngle = mix(contactShoulder - 15, 5, u);
|
|
144
|
+
flexion = mix(25, 56, u);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
transform('deny-upper-group', `rotate(${n(shoulderAngle)})`);
|
|
149
|
+
transform('deny-forearm-group', `translate(0 ${upper}) rotate(${n(-flexion)})`);
|
|
150
|
+
}
|
|
151
|
+
function draw(milliseconds: number, still = false) {
|
|
152
|
+
const ms = Number.isFinite(milliseconds) ? Math.max(0, milliseconds) : 0;
|
|
153
|
+
const t = ms / 1000;
|
|
154
|
+
const intro = ms < INTRO_DURATION && !still;
|
|
155
|
+
const p = still ? CONTACT_TIME / 1000 : Math.max(0, (ms - INTRO_DURATION) % LOOP_DURATION) / 1000;
|
|
156
|
+
|
|
157
|
+
transform('dog', 'translate(354 567)');
|
|
158
|
+
transform('dog-shadow', 'translate(0 0)');
|
|
159
|
+
attr('dog-shadow', 'ry', 11);
|
|
160
|
+
const reveal = intro ? smoother(clamp((t - 2.3) / 2.6)) : 1;
|
|
161
|
+
transform('camera', `translate(${n(mix(-118, 0, reveal))} ${n(mix(-396, 0, reveal))}) scale(${n(mix(1.7, 1, reveal))})`);
|
|
162
|
+
const rumble = intro ? Math.sin(Math.PI * clamp((t - .65) / 1.65)) ** 2 : 0;
|
|
163
|
+
const vibration = rumble * Math.sin(t * 30);
|
|
164
|
+
transform('belly', `translate(-43 -78) scale(${n(1 + vibration * .048)} ${n(1 - vibration * .022)}) translate(43 78)`);
|
|
165
|
+
opacity('hunger', rumble * (.7 + Math.sin(t * 22) * .3));
|
|
166
|
+
transform('hunger', `translate(${n(vibration * 1.6)} 0)`);
|
|
167
|
+
opacity('tongue', 0);
|
|
168
|
+
const paw = intro ? { x: 51, y: -8 } : pointTrack([
|
|
169
|
+
[0, 51, -8], [1, 51, -8], [1.45, 84, -98], [2.05, 144, -201],
|
|
170
|
+
[2.7, 185, -251], [3, 185, -251], [3.14, 164, -214],
|
|
171
|
+
[3.46, 101, -103], [3.98, 51, -8], [6.4, 51, -8],
|
|
172
|
+
], p);
|
|
173
|
+
const shoulder = { x: 20, y: -155 };
|
|
174
|
+
const joint = elbow(shoulder, paw, 95, 112, 1);
|
|
175
|
+
attr('dog-arm', 'd', bentLimb(shoulder, joint, paw, 17, 13, 10));
|
|
176
|
+
attr('dog-arm-fold', 'd', `M${xy(add(shoulder, { x: 5, y: 0 }))}L${xy(add(joint, { x: 4, y: 0 }))}L${xy(add(paw, { x: 3, y: -1 }))}`);
|
|
177
|
+
const pawAngle = intro ? 0 : track([[0, 0], [1, 0], [2.7, -15], [3, -15], [3.2, 25], [3.98, 0], [6.4, 0]], p);
|
|
178
|
+
transform('dog-paw', `translate(${xy(paw)}) rotate(${n(pawAngle)})`);
|
|
179
|
+
const headAngle = intro ? track([[0, 6], [.65, 6], [1.35, 15], [2.3, 10], [3.8, -5], [5.4, -5]], t) : track([[0, -5], [1.1, -5], [2.7, -12], [3, -12], [3.3, 9], [3.85, 12], [4.8, 5], [5.8, -5], [6.4, -5]], p);
|
|
180
|
+
transform('dog-head', `rotate(${n(headAngle)} 19 -196)`);
|
|
181
|
+
const earAngle = intro ? vibration * 2 : track([[0, 0], [3, 0], [3.16, -12], [3.37, 8], [3.66, -3], [4, 0], [6.4, 0]], p);
|
|
182
|
+
transform('ear', `rotate(${n(earAngle)} 7 -245)`);
|
|
183
|
+
const wag = still || intro ? 0 : Math.sin(p * Math.PI * 4 / 6.4) * track([[0, 3], [2.65, 5], [3.15, 0], [4.4, 0], [5.5, 3], [6.4, 3]], p);
|
|
184
|
+
transform('tail', `rotate(${n(wag)} -61 -75)`);
|
|
185
|
+
const blink = still ? 1 : intro ? track([[0, 1], [1.15, 1], [1.24, .08], [1.34, 1], [5.4, 1]], t) : track([[0, 1], [3.03, 1], [3.11, .08], [3.27, 1], [4.65, 1], [4.75, .1], [4.86, 1], [6.4, 1]], p);
|
|
186
|
+
transform('dog-eye', `translate(58 -241) scale(1 ${n(blink)}) translate(-58 241)`);
|
|
187
|
+
const disappointment = intro ? 0 : track([[0, 0], [3, 0], [3.6, 1], [4.8, 1], [5.8, 0], [6.4, 0]], p);
|
|
188
|
+
attr('dog-mouth', 'd', `M72 -205 Q84 ${n(-212 - disappointment * 4)} 99 -207`);
|
|
189
|
+
transform('brow', `rotate(${n(intro ? 0 : track([[0, 0], [2.9, 0], [3.65, 14], [4.9, 10], [5.8, 0], [6.4, 0]], p))} 58 -254)`);
|
|
190
|
+
|
|
191
|
+
eatingArm(p, intro || still);
|
|
192
|
+
blockingArm(p, intro);
|
|
193
|
+
opacity('tap', intro || still ? 0 : track([[0, 0], [3, 0], [3.055, .8], [3.19, 0], [6.4, 0]], p));
|
|
194
|
+
if (frame) {
|
|
195
|
+
|
|
196
|
+
frame.dataset.phase = still ? 'blocked' : intro ? t < 2.3 ? 'hungry' : 'reveal' : p < 1 ? 'waiting' : p < 3 ? 'reaching' : p < 3.3 ? 'denied' : 'recovering';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { draw, still: () => draw(INTRO_DURATION + CONTACT_TIME, true) };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface ForbiddenPaperStory { setPaused(paused: boolean): void; dispose(): void }
|
|
203
|
+
|
|
204
|
+
export function createForbiddenPaperStory(art: SVGSVGElement, intro: boolean, onSettled: () => void): ForbiddenPaperStory {
|
|
205
|
+
const render = createForbiddenRenderer(art)
|
|
206
|
+
let elapsed = intro ? 0 : INTRO_DURATION
|
|
207
|
+
let settled = !intro
|
|
208
|
+
let paused = false
|
|
209
|
+
let disposed = false
|
|
210
|
+
let request = 0
|
|
211
|
+
let last: number | undefined
|
|
212
|
+
function tick(now: number) {
|
|
213
|
+
request = 0
|
|
214
|
+
if (disposed || paused) return
|
|
215
|
+
if (last !== undefined) elapsed += Math.min(80, Math.max(0, now - last))
|
|
216
|
+
last = now
|
|
217
|
+
render.draw(elapsed)
|
|
218
|
+
if (!settled && elapsed >= INTRO_DURATION) { settled = true; onSettled() }
|
|
219
|
+
request = requestAnimationFrame(tick)
|
|
220
|
+
}
|
|
221
|
+
function stop() { cancelAnimationFrame(request); request = 0; last = undefined }
|
|
222
|
+
render.draw(elapsed)
|
|
223
|
+
request = requestAnimationFrame(tick)
|
|
224
|
+
return {
|
|
225
|
+
setPaused(value) {
|
|
226
|
+
if (disposed || paused === value) return
|
|
227
|
+
paused = value
|
|
228
|
+
stop()
|
|
229
|
+
if (!paused) request = requestAnimationFrame(tick)
|
|
230
|
+
},
|
|
231
|
+
dispose() { disposed = true; stop() },
|
|
232
|
+
}
|
|
233
|
+
}
|