基于Vue3實(shí)現(xiàn)的圖片散落效果實(shí)例
背景
今天又是美好的摸魚一天,剛剛進(jìn)入職場,覺得一切都很新鮮,導(dǎo)師給的任務(wù)也不多(要是每天都是這樣就好了),于是開始帶薪學(xué)習(xí)。
做啥好呢
沒事在網(wǎng)上亂逛的時(shí)候,偶然間看到一個(gè)動(dòng)畫效果不錯(cuò),就決定上手做一些,簡單的說就是一個(gè)完整的圖片,在一段時(shí)間之后回突然破裂開來,覺得很有意思,就新建了一個(gè)文件夾。
出現(xiàn)問題
一下午的摸魚時(shí)光,間公司熙熙攘攘,我在其中卻格格不入(太閑了),不知多少人投來質(zhì)疑的眼光(這家伙不工作嗎),但我只沉浸在我的代碼里。終于勉強(qiáng)完成了一個(gè)不怎么丑的版本。

原理
圖片破裂效果說白了就是搞了100個(gè)div,每個(gè)div都有自己的背景圖片,通過backgroundPosition屬性來控制每個(gè)div的背景圖片方位,最后拼在一起,就像一張完整的圖片一樣,給每個(gè)div都加上動(dòng)畫效果,每個(gè)div的旋轉(zhuǎn)角度不同,移動(dòng)距離不同,移動(dòng)方位不同來讓整個(gè)圖片像玻璃一樣散開來。
HTML結(jié)構(gòu)
這里用到了兩個(gè)div,#break是用作為100個(gè)div的容器,#InBox是用來綁定下一張的背景圖片
<div id="animateBox" v-show="showImg">
<div id="break"></div>
<div id="InBox"></div>
</div>準(zhǔn)備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個(gè)div,每個(gè)div綁定長寬,給div添加背景圖片,使用backgroundPosition來讓整個(gè)div變得像一張圖片,給div綁定動(dòng)畫效果。
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后面會(huì)提到
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來讓當(dāng)前展示的div是哪一個(gè)
前面說過,InBox是展示的下一張圖片,在breakBox散落完成之后,讓breakBox的zIndex降低,展示出下一張圖片,隨后帶有100個(gè)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)
}
}每次動(dòng)畫完成之后會(huì)去調(diào)上面這個(gè)方法,為了能在div碎片破碎完畢,展示下一張圖片,使用定時(shí)器將該方法進(jìn)行延遲處理 4s是因?yàn)閐iv碎片在4s后完全消失。(動(dòng)畫在運(yùn)行70%的時(shí)候,透明度為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個(gè)div展示之后會(huì)出現(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實(shí)現(xiàn)的圖片散落效果的文章就介紹到這了,更多相關(guān)Vue3圖片散落內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何解決sass-loader和node-sass版本沖突的問題
這篇文章主要介紹了如何解決sass-loader和node-sass版本沖突的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
VUE搭建分布式醫(yī)療掛號系統(tǒng)后臺(tái)管理頁面示例步驟
這篇文章主要為大家介紹了分布式醫(yī)療掛號系統(tǒng)之搭建后臺(tái)管理系統(tǒng)頁面,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
vue使用showdown并實(shí)現(xiàn)代碼區(qū)域高亮的示例代碼
這篇文章主要介紹了vue使用showdown并實(shí)現(xiàn)代碼區(qū)域高亮的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
vue改變數(shù)據(jù)后數(shù)據(jù)變化頁面不刷新的解決方法
這篇文章主要給大家介紹了關(guān)于vue改變數(shù)據(jù)后數(shù)據(jù)變化頁面不刷新的解決方法,vue比較常見的坑就是數(shù)據(jù)(后臺(tái)返回)更新了,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07

