Vuetify:如何使图像弹跳

huangapple go评论54阅读模式
英文:

Vuetify: how to bounce an image

问题

I have a project which uses v-chip components and when I move them around and drop them elsewhere, I have used a 'bounce' transition to animate them:

<Transition name="bounce">
    <v-chip  ...

And the CSS:

.bounce-enter-active {
  animation: bounce-in 0.5s;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

This works well, but if I try the same technique with a v-img, there is no bounce. So, how do I bounce an image?

Later edit

@Moritz's answer below was very helpful, improved my 'bounce' and is a great answer to the question I originally posed.
However, it was @yoduh's comment that led me to the key problem. I had changed the way the images were being moved, so in effect the reactive v-img didn't get a look-in to the DOM change. So I now have simply:

<v-chip class="bounce-enter-active" ...

instead of:

<Transition name="bounce">
    <v-chip  ...

Maybe I should have added this as an answer, but it doesn't actually answer my original question!

英文:

I have a project which uses v-chip components and when I move them around and drop them elsewhere, I have used a 'bounce' transition to animate them:

&lt;Transition name=&quot;bounce&quot;&gt;
		&lt;v-chip  ......

and the css:

.bounce-enter-active {
  animation: bounce-in 0.5s;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

This works well, but if I try the same technique with a v-img there is no bounce. So, how do I bounce an image?

Later edit

@Moritz 's answer below was very helpful, improved my 'bounce' and is a great answer to the question I originally posed.
However, it was @yoduh 's comment that led me to the key problem. I had changed the way the images were being moved, so in effect the reactive v-img didn't get a look-in to the DOM change. So I now have simply:

&lt;v-chip  class=&quot;bounce-enter-active&quot; ......

instead of:

&lt;Transition name=&quot;bounce&quot;&gt;
		&lt;v-chip  ......

Maybe I should have added this as an answer, but it doesn't actually answer my original question!

答案1

得分: 1

大多数情况下,您看不到动画是因为它发生在图像加载的同时。请查看以下代码片段以查看差异:

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()

const app = {
  setup() {
    const isOn = ref(false)
    const loadUncached = ref(false)
    const showPreloaded = ref(false)
    const nonEmpty = ref(false)
    const rawSrc = 'https://www.neonpoodle.co.uk/cdn/shop/products/HeadInCloudsPink_620x.jpg?v=1554576513'
    const src = ref(rawSrc)

    const loadSrc = () => {
      const src = rawSrc + (loadUncached.value ? '?asfd=' + Math.random() : '')
      return !showPreloaded.value ? src : new Promise(resolve => {
        const img = new Image()
        img.onload = () => resolve(src)
        img.src = src
      })
    }
    setInterval(async () => {
      const doShow = !isOn.value
      if (doShow) {
        src.value = await loadSrc()
      }
      isOn.value = doShow

    }, 2000)

    return {
      isOn,
      loadUncached,
      showPreloaded,
      nonEmpty,
      src
    }
  }
}
createApp(app).use(vuetify).mount('#app')

如果要查看完整的动画,请确保在显示图像之前将图像加载到缓存中。一种简单的方法是将URL放入“Image”中,并等待“onloaded”事件,此时图像已加载到缓存中:

const preloadSrc = (src) => {
  return new Promise(resolve => {
    const img = new Image()
    img.onload = () => resolve(true)
    img.src = src
  })
}
showVImg.value = await preloadSrc('/the/image/url')

还有一个@load事件,但要将其与&lt;Transition&gt;v-if的组合一起使用可能会很麻烦。但是,由于您正在使用真正的CSS动画,您可以在动画完成后使用它(如果在动画完成后保留类名可接受的话):

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()

const app = {
  setup() {
    const rawSrc = 'https://www.quilkin.co.uk/shared/topright.png'
    const src = ref(null)
    const loaded = ref(false)

    setInterval(async () => {
      src.value = src.value ? null : rawSrc + '?asfd=' + Math.random()
      loaded.value = false
    }, 2000)

    return {src, loaded}
  }
}
createApp(app).use(vuetify).mount('#app')

此外,您还可以在v-img上设置背景颜色和固定的高度和宽度,这样在图像加载时会看到背景动画。

最后,为了确保动画使用图像而不是缩小的版本,请在CSS中将image-rendering设置为crisp-edges。似乎只需要在动画的0%关键帧上设置它:

@keyframes bounce-in {
  0% {
    transform: scale(0);
    image-rendering: crisp-edges;
  }
  ...
}
英文:

Most likely, you don't see the animation because it happens while the image still loads. Have a look at the snippet to see the difference:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-js -->

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()

const app = {
  setup() {
    const isOn = ref(false)
    const loadUncached = ref(false)
    const showPreloaded = ref(false)
    const nonEmpty = ref(false)
    const rawSrc = &#39;https://www.neonpoodle.co.uk/cdn/shop/products/HeadInCloudsPink_620x.jpg?v=1554576513&#39;
    const src = ref(rawSrc)

    const loadSrc = () =&gt; {
      const src = rawSrc + (loadUncached.value ? &#39;?asfd=&#39; + Math.random() : &#39;&#39;)
      return !showPreloaded.value ? src : new Promise(resolve =&gt; {
        const img = new Image()
        img.onload = () =&gt; resolve(src)
        img.src = src
      })
    }
    setInterval(async() =&gt; {
      const doShow = !isOn.value
      if (doShow) {
        src.value = await loadSrc()
      }
      isOn.value = doShow

    }, 2000)

    return {
      isOn,
      loadUncached,
      showPreloaded,
      nonEmpty,
      src
    }
  }

}
createApp(app).use(vuetify).mount(&#39;#app&#39;)

<!-- language: lang-css -->

.bounce-enter-active {
  animation: bounce-in 0.5s;
}

@keyframes bounce-in {
  0% {
    transform: scale(0);
    image-rendering: crisp-edges;
  }
  50% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

.non-empty{
  width: 100px;
  height: 100px;
  background: purple;
}

<!-- language: lang-html -->

&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css&quot; /&gt;
&lt;link href=&quot;https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css&quot; rel=&quot;stylesheet&quot;&gt;
&lt;div id=&quot;app&quot;&gt;
  &lt;v-app&gt;
    &lt;v-main class=&quot;ma-4&quot;&gt;
      &lt;div class=&quot;d-flex&quot;&gt;
        &lt;v-switch v-model=&quot;loadUncached&quot; label=&quot;load uncached (leads to stunted transition)&quot;&gt;&lt;/v-switch&gt;
        &lt;v-switch v-model=&quot;showPreloaded&quot; label=&quot;preload image (restores transition)&quot; :disabled=&quot;!loadUncached&quot;&gt;&lt;/v-switch&gt;
        &lt;v-switch v-model=&quot;nonEmpty&quot; label=&quot;set background&quot;&gt;&lt;/v-switch&gt;
      &lt;/div&gt;
      &lt;Transition name=&quot;bounce&quot;&gt;
        &lt;v-img v-if=&quot;isOn&quot; :src=&quot;src&quot; width=&quot;100&quot; :class=&quot;{&#39;non-empty&#39; : nonEmpty}&quot;&gt;&lt;/v-img&gt;
      &lt;/Transition&gt;
    &lt;/v-main&gt;
  &lt;/v-app&gt;
&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/vue@3/dist/vue.global.prod.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

If you want to see the full animation, make sure that the image is loaded into cache before you show it. A simple way to do it is to put the url into an Image and wait for the onloaded event, at which point the image is loaded into cache:

  const preloadSrc = (src) =&gt; {
    return new Promise(resolve =&gt; {
      const img = new Image()
      img.onload = () =&gt; resolve(true)
      img.src = src
    })
  }
  showVImg.value = await preloadSrc(&#39;/the/image/url&#39;)

There is also a @load event on v-img, but it is quite annoying to get it to work with a combination of &lt;Transition&gt; and v-if. But since you are using a real CSS animation, you could use it without &lt;Transition&gt; (if it is acceptable to leave the class after the animation finished):

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-js -->

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()

const app = {
  setup() {
    const rawSrc = &#39;https://www.quilkin.co.uk/shared/topright.png&#39;
    const src = ref(null)
    const loaded = ref(false)

    setInterval(async() =&gt; {
      src.value = src.value ? null : rawSrc + &#39;?asfd=&#39; + Math.random()
      loaded.value = false
    }, 2000)

    return {src, loaded}
  }

}
createApp(app).use(vuetify).mount(&#39;#app&#39;)

<!-- language: lang-css -->

.bounce {
  animation: bounce-in 1s;
}

@keyframes bounce-in {
  0% {
    transform: scale(0);
    image-rendering: crisp-edges;
  }
  50% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

<!-- language: lang-html -->

&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css&quot; /&gt;
&lt;link href=&quot;https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css&quot; rel=&quot;stylesheet&quot;&gt;
&lt;div id=&quot;app&quot;&gt;
  &lt;v-app&gt;
    &lt;v-main class=&quot;ma-4&quot;&gt;

      &lt;v-img
        v-if=&quot;src&quot;
        :src=&quot;src&quot;
        width=&quot;100&quot;
        :class=&quot;{&#39;bounce&#39; : loaded}&quot;
        @load=&quot;loaded = true&quot;
      &gt;&lt;/v-img&gt;
    &lt;/v-main&gt;
  &lt;/v-app&gt;
&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/vue@3/dist/vue.global.prod.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->


Alternatively, you could also set a background color and fixed height and width on the v-img, then you see that background animated while the image loads.


Finally, to make sure the animation uses the image and not a scaled-down version, set image-rendering to crisp-edges in your CSS. It seems to be enough to set it on the 0% keyframe of the animation:

@keyframes bounce-in {
  0% {
    transform: scale(0);
    image-rendering: crisp-edges;
  }
  ...

huangapple
  • 本文由 发表于 2023年5月6日 22:18:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76189377.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定