经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
基于Vue3实现的图片散落效果实例
来源:jb51  时间:2022/4/18 10:10:59  对本文有异议

背景

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

做啥好呢

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

出现问题

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

原理

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

HTML结构

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

  1. <div id="animateBox" v-show="showImg">
  2. <div id="break"></div>
  3. <div id="InBox"></div>
  4. </div>

准备5张图片

  1. import bgImg5 from '../../assets/img/1/y1.png'
  2. import bgImg4 from '../../assets/img/1/y2.png'
  3. import bgImg3 from '../../assets/img/1/y3.png'
  4. import bgImg2 from '../../assets/img/1/y4.png'
  5. import bgImg6 from '../../assets/img/1/y5.png'
  6. import { ref, onMounted, onUnmounted } from 'vue'
  7. let index = 0
  8. onMounted(() => {
  9. let imageSrcArr = [bgImg2, bgImg3, bgImg4, bgImg5, bgImg6]
  10. let imgloadPromiseArr: Array<Promise<HTMLImageElement>> = []
  11. let imageArr: Array<string> = []
  12. for (let i = 0; i < imageSrcArr.length; i++) {
  13. imgloadPromiseArr[i] = new Promise((resolve, reject) => {
  14. let img = new Image()
  15. img.src = imageSrcArr[i]
  16. img.onload = () => {
  17. resolve(img)
  18. }
  19. })
  20. }
  21. imgloadPromiseArr.forEach(item => {
  22. item.then(res => {
  23. imageArr.push(`url(${(<HTMLImageElement>res).currentSrc})`)
  24. index = imageArr.length
  25. })
  26. })
  27. })

创建div

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

  1. for (let i = 0; i < 100; i++) {
  2. let div = document.createElement('div')
  3. let div1 = document.createElement('div')
  4. div.style.width = '76px'
  5. div.style.height = '41px' // 这里为什么是41px后面会提到
  6. div1.style.width = '76px'
  7. div1.style.height = '40px'
  8. div1.style.overflow = 'hidden'
  9. div.style.boxSizing = 'border-box'
  10. div.style.backgroundImage = imageArr[0]
  11. let positionX = -(i % 10) * 76 + 'px'
  12. let positionY = -Math.floor(i / 10) * 40 + 'px'
  13. div.style.backgroundPosition = positionX + ' ' + positionY
  14. div.style.backgroundSize = '760px 400px'
  15. let style = document.styleSheets[0]
  16. style.insertRule(`@keyframes secondrotate${i}
  17. {
  18. 0%,30%{
  19. transform:scale(1)
  20. }
  21. 70%
  22. {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
  23. 100%
  24. {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
  25. }`)
  26. style.insertRule(`@keyframes secondrotateS${i}
  27. {
  28. 0%,32%{
  29. transform:scale(1);opacity:1;
  30. }70%
  31. {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
  32. (0.5 - Math.random()) * 500
  33. }px);opacity:0}
  34. 100%
  35. {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
  36. (0.5 - Math.random()) * 500
  37. }px);opacity:0}
  38.  
  39. }`)
  40. div1.style.animation = `secondrotateS${i} 4.5s ease-out infinite`
  41. div.style.animation = `secondrotate${i} 4.5s ease-out infinite`
  42. div.style.transformOrigin = `center center`
  43. div1.appendChild(div)
  44. dom.appendChild(div1)
  45. }

切换背景图片

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

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

  1. let count = 0
  2. let repeat = true
  3. let breakBox: HTMLDivElement = document.querySelector('#break')!
  4. let InBox: HTMLDivElement = document.querySelector('#InBox')!
  5. function changeImage(InBox: HTMLDivElement) {
  6. if (repeat) {
  7. breakBox.style.zIndex = '-10'
  8. count++
  9. count = count === index ? 0 : count
  10. repeat = false
  11. setTimeout(() => {
  12. repeat = true
  13. breakBox.style.zIndex = '100'
  14. let currentImageLength = count === index - 1 ? 0 : count + 1
  15. InBox.style.backgroundImage = imageArr[currentImageLength]
  16. }, 1000)
  17. }
  18. }

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

  1. const timer1 = ref<number>()
  2. const timer2 = ref<number>()
  3. for (let i = 0; i < 100; i++) {
  4. let div = document.createElement('div')
  5. let div1 = document.createElement('div')
  6. div.style.width = '76px'
  7. div.style.height = '41px'
  8. div1.style.width = '76px'
  9. div1.style.height = '40px'
  10. div1.style.overflow = 'hidden'
  11. div.style.boxSizing = 'border-box'
  12. div.style.backgroundImage = imageArr[0]
  13. let positionX = -(i % 10) * 76 + 'px'
  14. let positionY = -Math.floor(i / 10) * 40 + 'px'
  15. div.style.backgroundPosition = positionX + ' ' + positionY
  16. div.style.backgroundSize = '760px 400px'
  17. let style = document.styleSheets[0]
  18. style.insertRule(`@keyframes secondrotate${i}
  19. {
  20. 0%,30%{
  21. transform:scale(1)
  22. }
  23. 70%
  24. {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
  25. 100%
  26. {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
  27. }`)
  28. style.insertRule(`@keyframes secondrotateS${i}
  29. {
  30. 0%,32%{
  31. transform:scale(1);opacity:1;
  32. }70%
  33. {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
  34. (0.5 - Math.random()) * 500
  35. }px);opacity:0}
  36. 100%
  37. {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
  38. (0.5 - Math.random()) * 500
  39. }px);opacity:0}
  40.  
  41. }`)
  42. div1.style.animation = `secondrotateS${i} 4.5s ease-out infinite`
  43. div.style.animation = `secondrotate${i} 4.5s ease-out infinite`
  44. div.style.transformOrigin = `center center`
  45. div.addEventListener('animationstart', () => {
  46. timer1.value = setTimeout(() => {
  47. changeImage(InBox)
  48. div.style.backgroundImage = imageArr[count]
  49. }, 4000)
  50. })
  51. div.addEventListener('animationiteration', () => {
  52. timer2.value = setTimeout(() => {
  53. changeImage(InBox)
  54. div.style.backgroundImage = imageArr[count]
  55. }, 4000)
  56. })
  57. div1.appendChild(div)
  58. dom.appendChild(div1)
  59. }

div存在间隙的问题

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

代码详情

  1. <template>
  2. <div>
  3. <transition name="fadeIn">
  4. <div id="animateBox" v-show="showImg">
  5. <div id="break"></div>
  6. <div id="InBox"></div>
  7. </div>
  8. </transition>
  9. </div>
  10. </template>
  11.  
  12. <script setup lang="ts">
  13. import bgImg5 from '../../assets/img/1/y1.png'
  14. import bgImg4 from '../../assets/img/1/y2.png'
  15. import bgImg3 from '../../assets/img/1/y3.png'
  16. import bgImg2 from '../../assets/img/1/y4.png'
  17. import bgImg6 from '../../assets/img/1/y5.png'
  18. import { ref, onMounted, onUnmounted } from 'vue'
  19. const timer1 = ref<number>()
  20. const timer2 = ref<number>()
  21. const showImg = ref<boolean>(false)
  22.  
  23. onMounted(() => {
  24. let imageSrcArr = [bgImg2, bgImg3, bgImg4, bgImg5, bgImg6]
  25. let imgloadPromiseArr: Array<Promise<HTMLImageElement>> = []
  26. let imageArr: Array<string> = []
  27. for (let i = 0; i < imageSrcArr.length; i++) {
  28. imgloadPromiseArr[i] = new Promise((resolve, reject) => {
  29. let img = new Image()
  30. img.src = imageSrcArr[i]
  31. img.onload = () => {
  32. resolve(img)
  33. }
  34. })
  35. }
  36. imgloadPromiseArr.forEach(item => {
  37. item.then(res => {
  38. imageArr.push(`url(${(<HTMLImageElement>res).currentSrc})`)
  39. index = imageArr.length
  40. })
  41. })
  42. showImg.value = true
  43. let repeat = true
  44. function changeImage(InBox: HTMLDivElement) {
  45. if (repeat) {
  46. breakBox.style.zIndex = '-10'
  47. count++
  48. count = count === index ? 0 : count
  49. repeat = false
  50. setTimeout(() => {
  51. repeat = true
  52. breakBox.style.zIndex = '100'
  53. let currentImageLength = count === index - 1 ? 0 : count + 1
  54. InBox.style.backgroundImage = imageArr[currentImageLength]
  55. }, 1000)
  56. }
  57. }
  58. let count = 0
  59. let index = 0
  60. let breakBox: HTMLDivElement = document.querySelector('#break')!
  61. let InBox: HTMLDivElement = document.querySelector('#InBox')!
  62. InBox.style.backgroundImage = imageArr[1]
  63. const appendDom = (dom: HTMLElement) => {
  64. for (let i = 0; i < 100; i++) {
  65. let div = document.createElement('div')
  66. let div1 = document.createElement('div')
  67. div.style.width = '76px'
  68. div.style.height = '41px'
  69. div1.style.width = '76px'
  70. div1.style.height = '40px'
  71. div1.style.overflow = 'hidden'
  72. div.style.boxSizing = 'border-box'
  73. div.style.backgroundImage = imageArr[0]
  74. let positionX = -(i % 10) * 76 + 'px'
  75. let positionY = -Math.floor(i / 10) * 40 + 'px'
  76. div.style.backgroundPosition = positionX + ' ' + positionY
  77. div.style.backgroundSize = '760px 400px'
  78. let style = document.styleSheets[0]
  79. style.insertRule(`@keyframes secondrotate${i}
  80. {
  81. 0%,30%{
  82. transform:scale(1)
  83. }
  84. 70%
  85. {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
  86. 100%
  87. {transform: rotateX(${180 + Math.random() * 720}deg) rotateY(${180 + Math.random() * 720}deg)}
  88. }`)
  89. style.insertRule(`@keyframes secondrotateS${i}
  90. {
  91. 0%,32%{
  92. transform:scale(1);opacity:1;
  93. }70%
  94. {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
  95. (0.5 - Math.random()) * 500
  96. }px);opacity:0}
  97. 100%
  98. {transform: translateZ(${300 + Math.random() * 1500}px) translate(${(0.5 - Math.random()) * 500}px,${
  99. (0.5 - Math.random()) * 500
  100. }px);opacity:0}
  101.  
  102. }`)
  103. div1.style.animation = `secondrotateS${i} 4.5s ease-out infinite`
  104. div.style.animation = `secondrotate${i} 4.5s ease-out infinite`
  105. div.style.transformOrigin = `center center`
  106. div.addEventListener('animationstart', () => {
  107. timer1.value = setTimeout(() => {
  108. changeImage(InBox)
  109. div.style.backgroundImage = imageArr[count]
  110. }, 4000)
  111. })
  112. div.addEventListener('animationiteration', () => {
  113. timer2.value = setTimeout(() => {
  114. changeImage(InBox)
  115. div.style.backgroundImage = imageArr[count]
  116. }, 4000)
  117. })
  118. div1.appendChild(div)
  119. dom.appendChild(div1)
  120. }
  121. }
  122. appendDom(breakBox)
  123. })
  124. onUnmounted(() => {
  125. typeof timer1 === 'number' && clearTimeout(timer1)
  126. typeof timer2 === 'number' && clearTimeout(timer2)
  127. })
  128. </script>
  129.  
  130. <style scoped lang="scss">
  131. @import url('../../css/comment/animate.css');
  132. #animateBox {
  133. width: 100vw;
  134. height: calc(100vh - 50px);
  135. // background-color: rgba(255, 255, 255, 0.6);
  136. #break {
  137. position: absolute;
  138. left: 0;
  139. top: 0;
  140. right: 0;
  141. bottom: 0;
  142. margin: auto;
  143. width: 760px;
  144. height: 400px;
  145. display: flex;
  146. perspective: 1000px;
  147. transform-style: preserve-3d;
  148. flex-wrap: wrap;
  149. z-index: 100;
  150. }
  151. #InBox {
  152. position: absolute;
  153. left: 0;
  154. top: 0;
  155. right: 0;
  156. bottom: 0;
  157. margin: auto;
  158. width: 760px;
  159. height: 400px;
  160. display: flex;
  161. perspective: 1000px;
  162. transform-style: preserve-3d;
  163. flex-wrap: wrap;
  164. z-index: 10;
  165. background-size: 760px 400px;
  166. }
  167. }
  168. </style>

总结

到此这篇关于基于Vue3实现的图片散落效果的文章就介绍到这了,更多相关Vue3图片散落内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号