基于Vue3實現(xiàn)的圖片散落效果實例
背景
今天又是美好的摸魚一天,剛剛進入職場,覺得一切都很新鮮,導(dǎo)師給的任務(wù)也不多(要是每天都是這樣就好了),于是開始帶薪學(xué)習(xí)。
做啥好呢
沒事在網(wǎng)上亂逛的時候,偶然間看到一個動畫效果不錯,就決定上手做一些,簡單的說就是一個完整的圖片,在一段時間之后回突然破裂開來,覺得很有意思,就新建了一個文件夾。
出現(xiàn)問題
一下午的摸魚時光,間公司熙熙攘攘,我在其中卻格格不入(太閑了),不知多少人投來質(zhì)疑的眼光(這家伙不工作嗎),但我只沉浸在我的代碼里。終于勉強完成了一個不怎么丑的版本。
原理
圖片破裂效果說白了就是搞了100個div,每個div都有自己的背景圖片,通過backgroundPosition屬性來控制每個div的背景圖片方位,最后拼在一起,就像一張完整的圖片一樣,給每個div都加上動畫效果,每個div的旋轉(zhuǎn)角度不同,移動距離不同,移動方位不同來讓整個圖片像玻璃一樣散開來。
HTML結(jié)構(gòu)
這里用到了兩個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 }) }) })
創(chuàng)建div
通過createElement創(chuàng)建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) } }
每次動畫完成之后會去調(diào)上面這個方法,為了能在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存在間隙的問題
在100個div展示之后會出現(xiàn)這樣的線,在經(jīng)過多次嘗試之后,找到了方法,將div的高度變大,div1設(shè)置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 lang="ts"> 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 lang="scss"> @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>
總結(jié)
到此這篇關(guān)于基于Vue3實現(xiàn)的圖片散落效果的文章就介紹到這了,更多相關(guān)Vue3圖片散落內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何解決sass-loader和node-sass版本沖突的問題
這篇文章主要介紹了如何解決sass-loader和node-sass版本沖突的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04VUE搭建分布式醫(yī)療掛號系統(tǒng)后臺管理頁面示例步驟
這篇文章主要為大家介紹了分布式醫(yī)療掛號系統(tǒng)之搭建后臺管理系統(tǒng)頁面,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04vue使用showdown并實現(xiàn)代碼區(qū)域高亮的示例代碼
這篇文章主要介紹了vue使用showdown并實現(xiàn)代碼區(qū)域高亮的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10vue改變數(shù)據(jù)后數(shù)據(jù)變化頁面不刷新的解決方法
這篇文章主要給大家介紹了關(guān)于vue改變數(shù)據(jù)后數(shù)據(jù)變化頁面不刷新的解決方法,vue比較常見的坑就是數(shù)據(jù)(后臺返回)更新了,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-07-07