• 技术文章 >web前端 >Vue.js

    手把手教你使用Vue3实现图片散落效果

    青灯夜游青灯夜游2022-04-27 10:57:19转载838
    基于Vue3怎么实现图片散落效果?下面本篇文章给大家介绍一下使用Vue3实现图片散落效果的方法,希望对大家有所帮助!

    今天又是美好的摸鱼一天,刚刚进入职场,觉得一切都很新鲜,导师给的任务也不多(要是每天都是这样就好了),于是开始带薪学习。(学习视频分享:vuejs教程

    做啥好呢

    没事在网上乱逛的时候,偶然间看到一个动画效果不错,就决定上手做一些,简单的说就是一个完整的图片,在一段时间之后回突然破裂开来,觉得很有意思,就新建了一个文件夹。

    出现问题

    一下午的摸鱼时光,间公司熙熙攘攘,我在其中却格格不入(太闲了),不知多少人投来质疑的眼光(这家伙不工作吗),但我只沉浸在我的代码里。终于勉强完成了一个不怎么丑的版本。

    1.png

    原理

    图片破裂效果说白了就是搞了100个div,每个div都有自己的背景图片,通过backgroundPosition属性来控制每个div的背景图片方位,最后拼在一起,就像一张完整的图片一样,给每个div都加上动画效果,每个div的旋转角度不同,移动距离不同,移动方位不同来让整个图片像玻璃一样散开来。

    HTML结构

    这里用到了两个div,#break是用作为100个div的容器,#InBox是用来绑定下一张的背景图片

    <div id="animateBox" v-show="showImg">
            <div id="break"></div>
            <div id="InBox"></div>
    </div>

    准备5张图片

    import bgImg5 from '../../assets/img/1/y1.png'
    import bgImg4 from '../../assets/img/1/y2.png'
    import bgImg3 from '../../assets/img/1/y3.png'
    import bgImg2 from '../../assets/img/1/y4.png'
    import bgImg6 from '../../assets/img/1/y5.png'
    import { ref, onMounted, onUnmounted } from 'vue'
    let index = 0
    onMounted(() => {
      let imageSrcArr = [bgImg2, bgImg3, bgImg4, bgImg5, bgImg6]
      let imgloadPromiseArr: Array<Promise<HTMLImageElement>> = []
      let imageArr: Array<string> = []
      for (let i = 0; i < imageSrcArr.length; i++) {
        imgloadPromiseArr[i] = new Promise((resolve, reject) => {
          let img = new Image()
          img.src = imageSrcArr[i]
          img.onload = () => {
            resolve(img)
          }
        })
      }
      imgloadPromiseArr.forEach(item => {
        item.then(res => {
          imageArr.push(`url(${(<HTMLImageElement>res).currentSrc})`)
          index = imageArr.length
        })
      })
      })

    创建div

    通过createElement创建200个div,每个div绑定长宽,给div添加背景图片,使用backgroundPosition来让整个div变得像一张图片,给div绑定动画效果。

    for (let i = 0; i < 100; i++) {
          let div = document.createElement('div')
          let div1 = document.createElement('div')
          div.style.width = '76px'
          div.style.height = '41px' // 这里为什么是41px后面会提到
          div1.style.width = '76px'
          div1.style.height = '40px'
          div1.style.overflow = 'hidden'
          div.style.boxSizing = 'border-box'
          div.style.backgroundImage = imageArr[0]
          let positionX = -(i % 10) * 76 + 'px'
          let positionY = -Math.floor(i / 10) * 40 + 'px'
          div.style.backgroundPosition = positionX + ' ' + positionY
          div.style.backgroundSize = '760px 400px'
          let style = document.styleSheets[0]
          style.insertRule(`@keyframes secondrotate${i}
            {
                0%,30%{
                    transform:scale(1)
                }
                70%
                {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
                100%
                {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
            }`)
          style.insertRule(`@keyframes secondrotateS${i}
            {
                0%,32%{
                    transform:scale(1);opacity:1;
                }70%
                {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
            (0.5 - Math.random()) * 500
          }px);opacity:0}
                100%
                {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
            (0.5 - Math.random()) * 500
          }px);opacity:0}
    
            }`)
          div1.style.animation = `secondrotateS${i} 4.5s ease-out infinite`
          div.style.animation = `secondrotate${i} 4.5s ease-out infinite`
          div.style.transformOrigin = `center center`
          div1.appendChild(div)
          dom.appendChild(div1)
    }

    切换背景图片

    通过zIndex来让当前展示的div是哪一个

    前面说过,InBox是展示的下一张图片,在breakBox散落完成之后,让breakBox的zIndex降低,展示出下一张图片,随后带有100个div的breakBox完成下一张图片的渲染,zIndex提高,展示出来

     let count = 0
     let repeat = true
     let breakBox: HTMLDivElement = document.querySelector('#break')!
     let InBox: HTMLDivElement = document.querySelector('#InBox')!
      function changeImage(InBox: HTMLDivElement) {
        if (repeat) {
          breakBox.style.zIndex = '-10'
          count++
          count = count === index ? 0 : count
          repeat = false
          setTimeout(() => {
            repeat = true
            breakBox.style.zIndex = '100'
            let currentImageLength = count === index - 1 ? 0 : count + 1
            InBox.style.backgroundImage = imageArr[currentImageLength]
          }, 1000)
        }
      }

    每次动画完成之后会去调上面这个方法,为了能在div碎片破碎完毕,展示下一张图片,使用定时器将该方法进行延迟处理 4s是因为div碎片在4s后完全消失。(动画在运行70%的时候,透明度为0)

    const timer1 = ref<number>()
    const timer2 = ref<number>()
    for (let i = 0; i < 100; i++) {
          let div = document.createElement('div')
          let div1 = document.createElement('div')
          div.style.width = '76px'
          div.style.height = '41px'
          div1.style.width = '76px'
          div1.style.height = '40px'
          div1.style.overflow = 'hidden'
          div.style.boxSizing = 'border-box'
          div.style.backgroundImage = imageArr[0]
          let positionX = -(i % 10) * 76 + 'px'
          let positionY = -Math.floor(i / 10) * 40 + 'px'
          div.style.backgroundPosition = positionX + ' ' + positionY
          div.style.backgroundSize = '760px 400px'
          let style = document.styleSheets[0]
          style.insertRule(`@keyframes secondrotate${i}
            {
                0%,30%{
                    transform:scale(1)
                }
                70%
                {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
                100%
                {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
            }`)
          style.insertRule(`@keyframes secondrotateS${i}
            {
                0%,32%{
                    transform:scale(1);opacity:1;
                }70%
                {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
            (0.5 - Math.random()) * 500
          }px);opacity:0}
                100%
                {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
            (0.5 - Math.random()) * 500
          }px);opacity:0}
    
            }`)
          div1.style.animation = `secondrotateS${i} 4.5s ease-out infinite`
          div.style.animation = `secondrotate${i} 4.5s ease-out infinite`
          div.style.transformOrigin = `center center`
          div.addEventListener('animationstart', () => {
            timer1.value = setTimeout(() => {
              changeImage(InBox)
              div.style.backgroundImage = imageArr[count]
            }, 4000)
          })
          div.addEventListener('animationiteration', () => {
            timer2.value = setTimeout(() => {
              changeImage(InBox)
              div.style.backgroundImage = imageArr[count]
            }, 4000)
          })
          div1.appendChild(div)
          dom.appendChild(div1)
        }

    div存在间隙的问题

    2.png

    在100个div展示之后会出现这样的线,在经过多次尝试之后,找到了方法,将div的高度变大,div1设置overflow:hidden; 线回消失

    代码详情

    <template>
      <div>
        <transition name="fadeIn">
          <div id="animateBox" v-show="showImg">
            <div id="break"></div>
            <div id="InBox"></div>
          </div>
        </transition>
      </div>
    </template>
    
    <script setup>
    import bgImg5 from '../../assets/img/1/y1.png'
    import bgImg4 from '../../assets/img/1/y2.png'
    import bgImg3 from '../../assets/img/1/y3.png'
    import bgImg2 from '../../assets/img/1/y4.png'
    import bgImg6 from '../../assets/img/1/y5.png'
    import { ref, onMounted, onUnmounted } from 'vue'
    const timer1 = ref<number>()
    const timer2 = ref<number>()
    const showImg = ref<boolean>(false)
    
    onMounted(() => {
      let imageSrcArr = [bgImg2, bgImg3, bgImg4, bgImg5, bgImg6]
      let imgloadPromiseArr: Array<Promise<HTMLImageElement>> = []
      let imageArr: Array<string> = []
      for (let i = 0; i < imageSrcArr.length; i++) {
        imgloadPromiseArr[i] = new Promise((resolve, reject) => {
          let img = new Image()
          img.src = imageSrcArr[i]
          img.onload = () => {
            resolve(img)
          }
        })
      }
      imgloadPromiseArr.forEach(item => {
        item.then(res => {
          imageArr.push(`url(${(<HTMLImageElement>res).currentSrc})`)
          index = imageArr.length
        })
      })
      showImg.value = true
      let repeat = true
      function changeImage(InBox: HTMLDivElement) {
        if (repeat) {
          breakBox.style.zIndex = '-10'
          count++
          count = count === index ? 0 : count
          repeat = false
          setTimeout(() => {
            repeat = true
            breakBox.style.zIndex = '100'
            let currentImageLength = count === index - 1 ? 0 : count + 1
            InBox.style.backgroundImage = imageArr[currentImageLength]
          }, 1000)
        }
      }
      let count = 0
      let index = 0
      let breakBox: HTMLDivElement = document.querySelector('#break')!
      let InBox: HTMLDivElement = document.querySelector('#InBox')!
      InBox.style.backgroundImage = imageArr[1]
      const appendDom = (dom: HTMLElement) => {
        for (let i = 0; i < 100; i++) {
          let div = document.createElement('div')
          let div1 = document.createElement('div')
          div.style.width = '76px'
          div.style.height = '41px'
          div1.style.width = '76px'
          div1.style.height = '40px'
          div1.style.overflow = 'hidden'
          div.style.boxSizing = 'border-box'
          div.style.backgroundImage = imageArr[0]
          let positionX = -(i % 10) * 76 + 'px'
          let positionY = -Math.floor(i / 10) * 40 + 'px'
          div.style.backgroundPosition = positionX + ' ' + positionY
          div.style.backgroundSize = '760px 400px'
          let style = document.styleSheets[0]
          style.insertRule(`@keyframes secondrotate${i}
            {
                0%,30%{
                    transform:scale(1)
                }
                70%
                {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
                100%
                {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
            }`)
          style.insertRule(`@keyframes secondrotateS${i}
            {
                0%,32%{
                    transform:scale(1);opacity:1;
                }70%
                {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
            (0.5 - Math.random()) * 500
          }px);opacity:0}
                100%
                {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
            (0.5 - Math.random()) * 500
          }px);opacity:0}
    
            }`)
          div1.style.animation = `secondrotateS${i} 4.5s ease-out infinite`
          div.style.animation = `secondrotate${i} 4.5s ease-out infinite`
          div.style.transformOrigin = `center center`
          div.addEventListener('animationstart', () => {
            timer1.value = setTimeout(() => {
              changeImage(InBox)
              div.style.backgroundImage = imageArr[count]
            }, 4000)
          })
          div.addEventListener('animationiteration', () => {
            timer2.value = setTimeout(() => {
              changeImage(InBox)
              div.style.backgroundImage = imageArr[count]
            }, 4000)
          })
          div1.appendChild(div)
          dom.appendChild(div1)
        }
      }
      appendDom(breakBox)
    })
    onUnmounted(() => {
      typeof timer1 === 'number' && clearTimeout(timer1)
      typeof timer2 === 'number' && clearTimeout(timer2)
    })
    </script>
    
    <style scoped>
    @import url('../../css/comment/animate.css');
    #animateBox {
      width: 100vw;
      height: calc(100vh - 50px);
      //  background-color: rgba(255, 255, 255, 0.6);
      #break {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        width: 760px;
        height: 400px;
        display: flex;
        perspective: 1000px;
        transform-style: preserve-3d;
        flex-wrap: wrap;
        z-index: 100;
      }
      #InBox {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        width: 760px;
        height: 400px;
        display: flex;
        perspective: 1000px;
        transform-style: preserve-3d;
        flex-wrap: wrap;
        z-index: 10;
        background-size: 760px 400px;
      }
    }
    </style>

    (学习视频分享:web前端开发编程入门

    以上就是手把手教你使用Vue3实现图片散落效果的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:Vue
    上一篇:vue中有缓存机制吗 下一篇:为什么用vue的反向代理
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• 了解vue中的单项数据流和双向数据绑定,两种冲突吗?• 手把手带你使用Vue + Laravel开发一个简单的 CRUD 应用• 总结分享Vue中实现组件间通讯的多种方式,再也不怕面试了!• vue中如何优雅的封装第三方组件?封装方法介绍• 聊聊Vue的新型前端构建工具 Vite(初体验)• vue3为什么快?vue3的效率提升主要在哪方面?
    1/1

    PHP中文网