如何在满足条件时每次从头开始显示 GIF 动画 – Vuejs 3 组合 API

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

How to show a GIF animation from the start every time condition is met - Vuejs 3 composition API

问题

以下是您要翻译的内容:

我有一个组件负责在满足v-if="isLoading"条件时显示一个.gif加载动画。

这个组件(SpinnerLoader)如下所示:

<template>
  <div>
    <img :src="spinnerUrl" class="custom-size" />
  </div>
</template>

<script setup lang="ts">
import spinnerUrl from '@/assets/spinnerUrl.gif';
</script>

我在另一个组件模板中将其用作子组件,如下所示:

<template>
  <div>
    <SpinnerLoader v-if="isLoading" />
    <!-- 其他代码 -->
  </div>
</template>

isLoading 存在于此父组件的 script 部分,显然是一个布尔值。

我的问题是,.gif动画仅在首次满足条件时从头开始播放。之后,动画仅在后台继续运行,当再次满足条件时不再从头开始播放。

我应该如何修改代码,以使每次满足条件时都能从头开始显示加载动画?

更新

正如@MoritzRingler在评论中提到的:在Firefox中这个功能正常工作,但不知何故在Chrome中不起作用。为什么会这样?如何解决?

英文:

I have a component which is in charge of showing a .gif spinner every time a v-if=&quot;isLoading&quot; condition is met.

The component (SpinnerLoader) looks like so:

&lt;template&gt;
  &lt;div&gt;
    &lt;img :src=&quot;spinnerUrl&quot; class=&quot;custom-size&quot; /&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup lang=&quot;ts&quot;&gt;
import spinnerUrl from &#39;@/assets/spinnerUrl.gif&#39;;
&lt;/script&gt;

And I use it as a child component in another component template like so:

&lt;template&gt;
  &lt;div&gt;
    &lt;SpinnerLoader v-if=&quot;isLoading&quot; /&gt;
    &lt;!-- some more code --&gt;
  &lt;/div&gt;
&lt;/template&gt;

isLoading lives in the script of this parent component. And it is obviously a boolean.

The problem I have is that the .gif animation only starts from the beginning the first time the condition is met. After that, the animation just keeps going in the background and when the condition is met it does not show the animation from the start any more.

How can I modify the code so that the spinner .gif animation is shown from the start every time the condition is met ?

UPDATE

As mentioned by @MoritzRingler in the comments: this works properly in Firefox, but from some reason it does not in Chrome. Why would that be ? and how can it be solved ?

答案1

得分: 1

这与浏览器相关,在Chrome中发生,但在Firefox中没有。我认为我找到了一个很好的解决方案,基于此答案中推荐的方法(顺便说一句,它值得更多的赞)。

思路是将gif转换为data URL,然后在每次组件加载时随机化URL。这里的优势是图片不必每次加载,但对浏览器来说仍然看起来像是不同的图片。

有一个注意事项:如果图片托管在不同的服务器上并且没有启用CORS,这将失败(或需要解决方法)。

这是一个playground链接,在那里查看可能更容易,但我将在这里添加一些带有注释的代码供参考。


这是一个可组合函数,每次调用useRandomizedGifDataUrl()时都会提供一个新的URL。因此,在您的SpinnerLoader组件中,您只需调用该方法并将其分配给src属性:

<template>
    <img :src="src" class="custom-size" />
</template>

<script setup>
  import { useRandomizedGifDataUrl } from './loadingGif.js'
  const src = useRandomizedGifDataUrl()
</script>

以下是可组合函数的代码:

import { ref, watchEffect } from 'vue'
const url = 'https://...'
const dataUrl = ref('')

/**
 * 加载图像并将其转换为数据URL
 *  !!! 这对CORS很敏感 !!!
 */
const loadImageDataUrl = async () => {
    const blob = await fetch(url).then(res => res.blob())
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    const event = await new Promise(resolve => reader.onload = resolve)
    return event.currentTarget.result
}

/**
 * 提供对原始dataUrl的访问(在第一次调用时为空)
 */
export function useLoadingGifDataUrl() {
  if (!dataUrl.value) {
    loadImageDataUrl().then(imageUrl => { dataUrl.value = imageUrl })
  }
  return dataUrl
}

/**
 * 基于原始dataUrl构建随机数据URL,通过在数据头中添加随机数来实现
 */
export function useRandomizedGifDataUrl() {
  const dataUrl = useLoadingGifDataUrl()
  const randomizedDataUrl = ref()
  const head = 'data:image/gif;base64'
  const randomize = (url) => !url ? '' : url.replace(head, `${head};${Math.random()}`)
  watchEffect(() => randomizedDataUrl.value = randomize(dataUrl.value))
  return randomizedDataUrl
}

这可以进行改进和定制,例如避免使用watchEffect或在第一次加载期间调整行为,但总体上,这就是

英文:

So this is browser related, it occurs in Chrome, but not in Firefox. I think I found a good solution, based on the recommended approach from this answer (it deserves more upvotes btw).

The idea is to turn the gif into a data URL and then randomizing the URL every time the component loads. The advantage here is that the image does not have to be loaded every time, but it still looks like a different image to the browser.

There is one caveat: If the image is hosted on a different server and does not have CORS enabled, this will fail (or need workarounds).

Here is a link to a playground, might be easier to look at it there, but I'll add the code with some comments here for completeness.


It's a composable that will give you a new URL every time its useRandomizedGifDataUrl() is called. So in your SpinnerLoader component, you just have to call the method and assign it to the src:

&lt;template&gt;
    &lt;img :src=&quot;src&quot; class=&quot;custom-size&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
  import {useRandomizedGifDataUrl} from &#39;./loadingGif.js&#39;
  const src = useRandomizedGifDataUrl()
&lt;/script&gt;

And here it the composable:

import {ref, watchEffect} from &#39;vue&#39;
const url = &#39;https://...&#39;
const dataUrl = ref(&#39;&#39;)

/**
 * Loads an image and turns it into a data URL
 *  !!! this is sensitive to CORS !!!
 */
const loadImageDataUrl = async () =&gt; {
    const blob = await fetch(url).then(res =&gt; res.blob())
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    const event = await new Promise(resolve =&gt; reader.onload = resolve)
    return event.currentTarget.result
}

/**
 * Provides access to the original dataUrl (empty on first call)
 */
export function useLoadingGifDataUrl(){
  if (!dataUrl.value){
    loadImageDataUrl().then(imageUrl =&gt; {dataUrl.value = imageUrl})
  }
  return dataUrl
}

/**
 * Builds a random data URL based on the original dataUrl by 
 * adding a random number into the data header.
 */
export function useRandomizedGifDataUrl(){
  const dataUrl = useLoadingGifDataUrl()
  const randomizedDataUrl = ref()
  const head = &#39;data:image/gif;base64&#39;
  const randomize = (url) =&gt; !url ? &#39;&#39; : url.replace(head, `${head};${Math.random()}`)
  watchEffect(() =&gt; randomizedDataUrl.value = randomize(dataUrl.value))
  return randomizedDataUrl
}

This can be improved and tailored, for example by avoiding the watchEffect or adjusting behavior during the first load, but in general, this is how it seems to work.

huangapple
  • 本文由 发表于 2023年7月5日 00:32:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76614496.html
匿名

发表评论

匿名网友

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

确定