]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
docs: animated logo (#2035)
authorEduardo San Martin Morote <posva@users.noreply.github.com>
Thu, 11 Jul 2024 07:19:37 +0000 (09:19 +0200)
committerGitHub <noreply@github.com>
Thu, 11 Jul 2024 07:19:37 +0000 (09:19 +0200)
* docs: wip animated logo

* docs: better perf

* docs: idle state

* docs: idle at start

* docs: ok

packages/docs/.vitepress/theme/components/PiniaLogo.vue
packages/docs/.vitepress/theme/index.ts
packages/docs/api/pinia/functions/mapActions.md
packages/docs/package.json
pnpm-lock.yaml

index 99c200ffbe7b059edcaaa8c2b4a00aeea2328bcf..443bc8529425963e18b5ddc0aca31af11b88ad2f 100644 (file)
@@ -1,10 +1,11 @@
 <template>
   <svg
-    width="408"
-    height="520"
+    id="pinia-logo"
     viewBox="0 0 408 520"
     fill="none"
     xmlns="http://www.w3.org/2000/svg"
+    style="z-index: 1; width: 100%"
+    ref="svgEl"
   >
     <g class="leaves">
       <path
             d="M150.023 321.156C149.513 335.783 137.241 347.226 122.615 346.715C107.988 346.205 96.545 333.933 97.0557 319.307C97.5665 304.68 109.838 293.237 124.464 293.748C139.091 294.258 150.534 306.53 150.023 321.156Z"
             fill="white"
           />
-          <g class="eyeball">
-            <path
-              d="M141.046 320.343C140.719 329.726 132.847 337.067 123.463 336.739C114.08 336.411 106.739 328.539 107.067 319.156C107.395 309.773 115.267 302.432 124.65 302.76C134.033 303.087 141.374 310.959 141.046 320.343Z"
-              fill="black"
-            />
-            <path
-              d="M125.161 316.786C125.026 320.65 121.784 323.672 117.921 323.537C114.057 323.403 111.034 320.161 111.169 316.297C111.304 312.434 114.546 309.411 118.409 309.546C122.273 309.681 125.296 312.922 125.161 316.786Z"
-              fill="white"
-            />
+          <g clip-path="url(#eye-left-mask)">
+            <g class="eyeball">
+              <path
+                d="M141.046 320.343C140.719 329.726 132.847 337.067 123.463 336.739C114.08 336.411 106.739 328.539 107.067 319.156C107.395 309.773 115.267 302.432 124.65 302.76C134.033 303.087 141.374 310.959 141.046 320.343Z"
+                fill="black"
+              />
+              <path
+                d="M125.161 316.786C125.026 320.65 121.784 323.672 117.921 323.537C114.057 323.403 111.034 320.161 111.169 316.297C111.304 312.434 114.546 309.411 118.409 309.546C122.273 309.681 125.296 312.922 125.161 316.786Z"
+                fill="white"
+              />
+            </g>
           </g>
         </template>
         <path
             d="M279.944 325.693C279.433 340.32 267.162 351.763 252.536 351.252C237.909 350.742 226.466 338.47 226.977 323.844C227.487 309.217 239.759 297.774 254.385 298.285C269.012 298.795 280.455 311.067 279.944 325.693Z"
             fill="white"
           />
-          <g class="eyeball">
-            <path
-              d="M270.967 324.879C270.64 334.263 262.767 341.604 253.384 341.276C244.001 340.948 236.66 333.076 236.988 323.693C237.316 314.31 245.188 306.969 254.571 307.297C263.954 307.624 271.295 315.496 270.967 324.879Z"
-              fill="black"
-            />
-            <path
-              d="M255.082 321.323C254.947 325.187 251.705 328.209 247.842 328.074C243.978 327.939 240.955 324.698 241.09 320.834C241.225 316.971 244.467 313.948 248.33 314.083C252.194 314.218 255.217 317.459 255.082 321.323Z"
-              fill="white"
-            />
+          <g clip-path="url(#eye-right-mask)">
+            <g class="eyeball">
+              <path
+                d="M270.967 324.879C270.64 334.263 262.767 341.604 253.384 341.276C244.001 340.948 236.66 333.076 236.988 323.693C237.316 314.31 245.188 306.969 254.571 307.297C263.954 307.624 271.295 315.496 270.967 324.879Z"
+                fill="black"
+              />
+              <path
+                d="M255.082 321.323C254.947 325.187 251.705 328.209 247.842 328.074C243.978 327.939 240.955 324.698 241.09 320.834C241.225 316.971 244.467 313.948 248.33 314.083C252.194 314.218 255.217 317.459 255.082 321.323Z"
+                fill="white"
+              />
+            </g>
           </g>
         </template>
         <path
         d="M141.767 409.993C143.98 412.073 144.088 415.554 142.007 417.767L95.0074 467.767C92.927 469.98 89.4462 470.088 87.233 468.007C85.0197 465.927 84.9121 462.446 86.9925 460.233L133.993 410.233C136.073 408.02 139.554 407.912 141.767 409.993Z"
         fill="#ECB732"
       />
-      <path
-        fill-rule="evenodd"
-        clip-rule="evenodd"
-        v-if="!talking || talking == 'closed'"
-        d="M163.323 337.658C161.949 338.584 161.586 340.448 162.512 341.822C167.176 348.743 174.321 352.632 183.51 353.682C192.767 354.74 201.051 352.375 208.164 346.594C209.45 345.549 209.645 343.66 208.6 342.374C207.555 341.088 205.666 340.893 204.38 341.938C198.552 346.675 191.887 348.6 184.191 347.721C176.425 346.834 171.003 343.686 167.488 338.469C166.562 337.095 164.697 336.732 163.323 337.658Z"
-        fill="black"
-      />
-      <path
-        v-else
-        d="M205.5 350C205.5 355.761 203.379 359.178 200.18 361.288C196.798 363.518 191.88 364.5 186 364.5C180.12 364.5 175.202 363.518 171.82 361.288C168.621 359.178 166.5 355.761 166.5 350C166.5 347.536 168.262 344.446 171.9 341.84C175.456 339.292 180.447 337.5 186 337.5C191.553 337.5 196.544 339.292 200.1 341.84C203.738 344.446 205.5 347.536 205.5 350Z"
-        fill="#E77777"
-        stroke="black"
-        stroke-width="5"
-      />
+      <g class="mouth">
+        <path
+          class="smile"
+          fill-rule="evenodd"
+          clip-rule="evenodd"
+          d="M163.323 337.658C161.949 338.584 161.586 340.448 162.512 341.822C167.176 348.743 174.321 352.632 183.51 353.682C192.767 354.74 201.051 352.375 208.164 346.594C209.45 345.549 209.645 343.66 208.6 342.374C207.555 341.088 205.666 340.893 204.38 341.938C198.552 346.675 191.887 348.6 184.191 347.721C176.425 346.834 171.003 343.686 167.488 338.469C166.562 337.095 164.697 336.732 163.323 337.658Z"
+          fill="black"
+        />
+        <path
+          class="open"
+          d="M213.046 343.089C213.046 356.089 199.012 367.537 186.862 367.537C174.712 367.537 164.177 356.078 164.177 343.078C164.177 335.899 175.857 332.075 188.008 332.075C200.158 332.075 213.046 335.909 213.046 343.089Z"
+          fill="#E77777"
+        />
+      </g>
     </g>
 
     <defs>
         <stop stop-color="#FFE56C" />
         <stop offset="1" stop-color="#FFC63A" />
       </linearGradient>
+
+      <clipPath id="eye-right-mask">
+        <circle cy="325px" cx="254" r="27" />
+      </clipPath>
+      <clipPath id="eye-left-mask">
+        <circle cy="320" cx="124" r="27" />
+      </clipPath>
     </defs>
   </svg>
 </template>
 
 <script setup lang="ts">
-import { onMounted, onUnmounted, ref } from 'vue'
-import { useCounter } from '../stores/counter'
+import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
+import { useSpring } from 'vue-use-spring'
+import {
+  useMouse,
+  useEventListener,
+  useDebounceFn,
+  useIdle,
+  whenever,
+} from '@vueuse/core'
 
-const blinking = ref<'open' | 'closed'>('open')
-const talking = ref<'open' | 'closed'>('closed')
+const { x: mouseX, y: mouseY } = useMouse()
+const mousePos = useSpring(
+  reactive({
+    x: mouseX,
+    y: mouseY,
+  }),
+  {
+    mass: 1,
+    tension: 120,
+    friction: 34,
+    precision: 1,
+  }
+)
 
-const counter = useCounter()
+const { idle } = useIdle(3000)
+
+// make it like it's looking at the user when they are idle
+whenever(idle, () => {
+  const l = leftEyeCenter.value
+  const r = rightEyeCenter.value
+  mousePos.x = l.x + (r.x - l.x) / 2
+  mousePos.y = r.y
+})
+
+const svgEl = ref<SVGElement>()
+const leftEyeCenter = ref({ x: 0, y: 0 })
+const rightEyeCenter = ref({ x: 0, y: 0 })
+
+function computedEyesCenter() {
+  const svg = svgEl.value
+  if (svg) {
+    const leftEye = svg.querySelector<SVGElement>('.eye-left .eyeball')!
+    const leftEyeRect = leftEye.getBoundingClientRect()
+    leftEyeCenter.value = {
+      x: leftEyeRect.x + leftEyeRect.width / 2,
+      y: leftEyeRect.y + leftEyeRect.height / 2,
+    }
+
+    const rightEye = svg.querySelector<SVGElement>('.eye-right .eyeball')!
+    const rightEyeRect = rightEye.getBoundingClientRect()
+    rightEyeCenter.value = {
+      x: rightEyeRect.x + rightEyeRect.width / 2,
+      y: rightEyeRect.y + rightEyeRect.height / 2,
+    }
+  }
+}
+
+useEventListener('resize', useDebounceFn(computedEyesCenter, 750))
+
+const leftEyeHorizontalDistance = computed(() => {
+  return Math.min(1, Math.max(-1, (mousePos.x - leftEyeCenter.value.x) / 150))
+})
+const rightEyeHorizontalDistance = computed(() => {
+  return Math.min(1, Math.max(-1, (mousePos.x - rightEyeCenter.value.x) / 150))
+})
+const eyeVerticalDistance = computed(() => {
+  return Math.min(1, Math.max(-1, (mousePos.y - leftEyeCenter.value.y) / 100))
+})
+
+const blinking = ref<'open' | 'closed'>('open')
 
 const blinkTimer = 100
-const talkRate = 120
+// use a randomized blink interval for a more natural look
+const minBlinkInterval = 2000
+const maxBlinkInterval = 10000
 
 onMounted(() => {
+  nextTick(() => {
+    computedEyesCenter()
+    idle.value = true
+  })
+
   let timerId = setInterval(() => {
     let blinkState = 0
     function blinkHandler() {
@@ -217,8 +299,6 @@ onMounted(() => {
 
       if (blinkState % 2) {
         blinking.value = 'closed'
-        // counter.n++
-        counter.$patch({ n: counter.n + 1 })
         setTimeout(blinkHandler, blinkTimer * 1.7)
       } else if (blinkState < 4) {
         blinking.value = 'open'
@@ -228,97 +308,36 @@ onMounted(() => {
       }
     }
     setTimeout(blinkHandler, 0)
-  }, 10000)
+  }, (maxBlinkInterval - minBlinkInterval) / 2)
 
   onUnmounted(() => {
     clearInterval(timerId)
   })
-
-  let talkingTimer = setInterval(() => {
-    let blinkState = 0
-    function blinkHandler() {
-      blinkState++
-
-      if (blinkState % 2) {
-        talking.value = 'closed'
-        setTimeout(blinkHandler, talkRate)
-      } else if (blinkState < 10) {
-        talking.value = 'open'
-        setTimeout(blinkHandler, talkRate)
-      } else {
-        talking.value = 'closed'
-      }
-    }
-    setTimeout(blinkHandler, 0)
-  }, 5000)
-  onUnmounted(() => {
-    clearInterval(talkingTimer)
-  })
 })
 </script>
 
-<style>
-@keyframes leaves-move {
-  40% {
-    transform: rotate(0deg) scale(1);
-  }
-  45% {
-    transform: rotate(-2deg) scale3d(0.9, 1.05, 1) translateY(6%);
-  }
-  47% {
-    transform: scale3d(1.05, 0.95, 1) translateY(2%);
-  }
-  50% {
-    transform: rotate(3deg) scale3d(0.9, 1.05, 1);
-  }
-  55% {
-    transform: rotate(0) scale(1);
-  }
+<style scoped>
+.eye-left .eyeball {
+  transform: translate(
+    calc(12px * v-bind(leftEyeHorizontalDistance)),
+    calc(10px * v-bind(eyeVerticalDistance))
+  );
 }
 
-@keyframes rubberBand {
-  29% {
-    transform: scale3d(1, 1, 1);
-  }
-
-  30% {
-    transform: scale3d(1.1, 0.9, 1);
-  }
-
-  40% {
-    transform: scale3d(0.9, 1.1, 1);
-  }
-
-  50% {
-    transform: scale3d(1.05, 0.95, 1);
-  }
-
-  65% {
-    transform: scale3d(0.98, 1.02, 1);
-  }
-
-  75% {
-    transform: scale3d(1.02, 0.98, 1);
-  }
-
-  76% {
-    transform: scale3d(1, 1, 1);
-  }
+.eye-right .eyeball {
+  transform: translate(
+    calc(12px * v-bind(rightEyeHorizontalDistance)),
+    calc(10px * v-bind(eyeVerticalDistance))
+  );
 }
-</style>
 
-<style scoped>
-.leaves {
-  animation: 500ms ease-in-out infinite rubberBand;
-  animation: 3s ease-in-out 0s infinite normal leaves-move;
-  transform-origin: bottom;
+#pinia-logo .mouth .open {
+  visibility: hidden;
 }
-
-.body {
-  animation-duration: 500ms;
-  animation-delay: 0s;
-  animation-iteration-count: 0;
-  animation-name: rubberBand;
-  transform-origin: center;
+#pinia-logo:hover .mouth .open {
+  visibility: unset;
+}
+#pinia-logo:hover .mouth .smile {
+  visibility: hidden;
 }
 </style>
index 374d96f9a2c9d4e073609c2177ffdec0daf41d22..adb16c84a7d4d1ae63c252659a431472ae432294 100644 (file)
@@ -4,6 +4,8 @@ import DefaultTheme from 'vitepress/theme'
 import AsideSponsors from './components/AsideSponsors.vue'
 // import AsideSponsors from './components/AsideSponsors.vue'
 import TranslationStatus from 'vitepress-translation-helper/ui/TranslationStatus.vue'
+// import HomeSponsors from './components/HomeSponsors.vue'
+import PiniaLogo from './components/PiniaLogo.vue'
 import './styles/vars.css'
 import './styles/playground-links.css'
 import VueSchoolLink from './components/VueSchoolLink.vue'
@@ -20,6 +22,7 @@ const theme: Theme = {
   ...DefaultTheme,
   Layout() {
     return h(DefaultTheme.Layout, null, {
+      'home-hero-image': () => h('div', { class: 'image-src' }, h(PiniaLogo)),
       // 'home-features-after': () => h(HomeSponsors),
       'aside-ads-before': () => h(AsideSponsors),
       // 'layout-top': () => h(VuejsdeConfBanner),
index 66dc9a40da7813da4546e759354a183092bdbbd5..409c503bd775c682c3583d68bbe34d4a1c324e27 100644 (file)
@@ -64,11 +64,11 @@ export default {
   methods: {
     // other methods properties
     // useCounterStore has two actions named `increment` and `setCount`
-    ...mapActions(useCounterStore, { moar: 'increment', setIt: 'setCount' })
+    ...mapActions(useCounterStore, { more: 'increment', setIt: 'setCount' })
   },
 
   created() {
-    this.moar()
+    this.more()
     this.setIt(2)
   }
 }
index 5cdec393cc3c2039ed13f9c2b2b467d0b8e6cfcc..0145d3ba3f5ae0501ffa65110a78d9d8a1b881a4 100644 (file)
@@ -17,6 +17,7 @@
     "@vueuse/core": "^10.11.0",
     "pinia": "workspace:*",
     "vitepress": "1.2.3",
-    "vitepress-translation-helper": "^0.2.1"
+    "vitepress-translation-helper": "^0.2.1",
+    "vue-use-spring": "^0.1.1"
   }
 }
index b0d11da35ec86d9ebff9fa446b032da95aadbea4..9d80cf366dd990c0eef28c0332abca6df900f6fb 100644 (file)
@@ -126,6 +126,9 @@ importers:
       vitepress-translation-helper:
         specifier: ^0.2.1
         version: 0.2.1(vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/node@20.14.9)(@vue/composition-api@1.7.2(vue@3.4.30(typescript@5.5.2)))(postcss@8.4.38)(search-insights@2.14.0)(terser@5.31.1)(typescript@5.5.2))(vue@3.4.30(typescript@5.5.2))
+      vue-use-spring:
+        specifier: ^0.1.1
+        version: 0.1.1(@vue/composition-api@1.7.2(vue@3.4.30(typescript@5.5.2)))(vue@3.4.30(typescript@5.5.2))
 
   packages/nuxt:
     dependencies:
@@ -5410,6 +5413,17 @@ packages:
   vue-component-type-helpers@2.0.22:
     resolution: {integrity: sha512-gPr2Ba7efUwy/Vfbuf735bHSVdN4ycoZUCHfypkI33M9DUH+ieRblLLVM2eImccFYaWNWwEzURx02EgoXDBmaQ==}
 
+  vue-demi@0.11.4:
+    resolution: {integrity: sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==}
+    engines: {node: '>=12'}
+    hasBin: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
   vue-demi@0.14.8:
     resolution: {integrity: sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==}
     engines: {node: '>=12'}
@@ -5447,6 +5461,15 @@ packages:
     peerDependencies:
       typescript: '*'
 
+  vue-use-spring@0.1.1:
+    resolution: {integrity: sha512-SNzVt6aLbcJvYz5DDUbVIo2+SdipYpPQDXWtMExD420LQMWCHz8eNbZjdCIFWsUtPGexm0DGYZiVInFjr0ILsA==}
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^2.6.12 || ^3.0.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
   vue@3.4.30:
     resolution: {integrity: sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==}
     peerDependencies:
@@ -11426,6 +11449,12 @@ snapshots:
 
   vue-component-type-helpers@2.0.22: {}
 
+  vue-demi@0.11.4(@vue/composition-api@1.7.2(vue@3.4.30(typescript@5.5.2)))(vue@3.4.30(typescript@5.5.2)):
+    dependencies:
+      vue: 3.4.30(typescript@5.5.2)
+    optionalDependencies:
+      '@vue/composition-api': 1.7.2(vue@3.4.30(typescript@5.5.2))
+
   vue-demi@0.14.8(@vue/composition-api@1.7.2(vue@3.4.30(typescript@5.5.2)))(vue@3.4.30(typescript@5.5.2)):
     dependencies:
       vue: 3.4.30(typescript@5.5.2)
@@ -11458,6 +11487,13 @@ snapshots:
       semver: 7.6.2
       typescript: 5.5.2
 
+  vue-use-spring@0.1.1(@vue/composition-api@1.7.2(vue@3.4.30(typescript@5.5.2)))(vue@3.4.30(typescript@5.5.2)):
+    dependencies:
+      vue: 3.4.30(typescript@5.5.2)
+      vue-demi: 0.11.4(@vue/composition-api@1.7.2(vue@3.4.30(typescript@5.5.2)))(vue@3.4.30(typescript@5.5.2))
+    optionalDependencies:
+      '@vue/composition-api': 1.7.2(vue@3.4.30(typescript@5.5.2))
+
   vue@3.4.30(typescript@5.5.2):
     dependencies:
       '@vue/compiler-dom': 3.4.30