前言 在这篇文章中 ,我们学习了如何设置固定数量的动态背景图。接下来,我们在这篇文章的基础上继续增加功能,让动态背景图的数量也不是固定的、图像也不是固定的。
动态的背景图 在这里,我个人想从两个方面解释动态 。
一方面是当前呈现的图片是不断轮转的,这是轮播图的基本定义。
另一方面就是呈现的图片并不是一开始就固定好的,而是在轮播的过程中不断更新的。
第一个方面我们已经实现了,在这里也就不多赘述。接下来我们思考如何实现第二个方面。
更新图库 为了响应速度更快,我个人倾向于将loading
期间加载的内容固定下来,主要是为了能够让loading
期间有一个基本的轮播图。
然后,在第一张图片轮转消失后,更新第一张图片。这样的话,显示的是第二张图片,而第一张图片已经在你看不见的地方更新掉了。当回到第一张图片的时候,我们发现轮转出来的是一张新的图片。
这样就能够分两个阶段给出一大批背景图片了。
时间计算 还记得上一篇文章吗,我们使用 的时间作为一整轮的轮播动画时间。这也就意味着,算上淡入淡出,一张图片的整个周期也就是
为了让第一张图片在这个周期之外更新,我们所需要做的,首先就是确定 的周期执行更新动作。
但是,光有这个 可不够。我们都知道,当浏览器加载过一次图片之后,下一次加载该图片将从缓存中直接读取,也就是我们寻常所说的命中缓存 。
命中后,加载速度是相当迅猛的。我们不能赌他的枪里没有子弹。所以我们还需要额外设计一个定时器,要让这个 的周期稍微缓上一缓,才能够保证更新时间是处于我们所看不到的时间段内。
还记得上一篇文章的流程图吗:
graph LR
A[第一张图片]--25%-->B[第二张图片]--25%-->C[第三张图片]--25%-->D[第四张图片]--25%-->A
我们主要是额外增加一个逻辑,让图片能够跟着时间有些修改:
graph LR
subgraph 轮播图
A[第一张图片]--25%-->B[第二张图片]--25%-->C[第三张图片]--25%-->D[第四张图片]--25%-->A
end
subgraph 同步更新
E[等待2s]-->F[等待25%的动画结束]-->G[更新图片]-->E
end
subgraph 计数器
H[[count = 0]] --> I[count] --> K[count mod 4]
G --> I
K -->|=0| A
K -->|=1| B
K -->|=2| C
K -->|=3| D
end
当然,除了mod
计算,还有满 归零的方法,可以画成这样:
graph LR
subgraph 轮播图
A[第一张图片]--25%-->B[第二张图片]--25%-->C[第三张图片]--25%-->D[第四张图片]--25%-->A
end
subgraph 同步更新
E[等待2s]-->F[等待25%的动画结束]-->G[更新图片]-->E
end
subgraph 计数器
G-->I{count = 4 ?}--yes-->J[count - 4]-->H-->A
O[[count = 0]] --> I
H[count = 0]
L[count = 1] --> B
M[count = 2] --> C
N[count = 3] --> D
I--no-->K[+1]-->|+1| L & M & N
end
需要注意的是,在这里更新图片并不是与图片动画同步的。我这边设置的是当图片转过去之后再更改,也就是说,这里还有一个判断当前轮转索引与修改索引之间的关系。
graph LR
subgraph 轮播图
A[第一张图片]--25%-->B[第二张图片]--25%-->C[第三张图片]--25%-->D[第四张图片]--25%-->A
end
subgraph 同步更新
E[等待2s]-->F[等待25%的动画结束]-->G[更新图片]-->E
end
subgraph 计数器
I--(count+2)%4-->P[index]--更新即触发-->G
I{count = 4 ?}--yes-->J[count - 4]-->H-->A
O[[count = 0]] --> I
H[count = 0]
L[count = 1] --> B
M[count = 2] --> C
N[count = 3] --> D
I--no-->K[+1]-->|+1| L & M & N
end
修改src属性还是直接将整个img标签替换掉 在这里,主要讨论的就是更新图片过程中,我们是修改src
还是直接将整个img
标签替换掉。
如果你有一定的经验,你会发现,如果src
相同,则会直接命中缓存,不会更新图片。对于我们目前而言似乎确实可以使用,因为我们的背景图片并不会随意修改,而是始终保持链接。 当然,如果你有更多的经验,你会想到,在GET
请求中加上一个时间戳,浏览器就会误以为这是一次新的请求,能够保证始终更新图片。
而如果是整个img
直接替换掉,浏览器就会开始渲染流程,能够确保本次请求是一定能够被加载的。
当然,每种方法都有自己的优势跟弊端,这就需要各位自行判断了。接下来我将以替换整个img
为例进行说明。
代码实现 好了,又到了show me the code
环节。
保存位置 我们还是选择source/js/utils.js
文件,在我们最开始增加的addBackgroundImageDiv
方法的最下面继续增加:
增加代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 let count = 0 , index = 0 ;const imgUrls = { 'pc' : [ '/race-miku.jpg' , '/masuri-miku.jpg' , '/planet-miku.jpg' , '/4mikus.jpg' , '/84672028_p0.jpg' , '/84932457_p0.png' ] } document .onreadystatechange = function ( ) { if (document .readyState === 'complete' ) { console .log ('done' ); let imgChangeInterval = null ; let imageChangeTimeOut = setTimeout (function ( ) { console .log ('timeout' ); if (imgChangeInterval != null ) { clearInterval (imgChangeInterval); imgChangeInterval = null ; } imgChangeInterval = setInterval (function ( ) { index = (count + 2 ) % 4 const imageDivElement = document .getElementById ("image-scroller" ).children [count % 4 ]; let sampleImg = Math .floor (Math .random () * imgUrls[DEVICES [0 ]].length ); imageDivElement.innerHTML = "<img" + " src='" + BASE_URL + DEVICES [0 ] + imgUrls[DEVICES [0 ]][sampleImg] + "'" + " style='width: 100%; height: 100%;'" + " alt='network broken?' />" ; console .log (`changed, now is ${count % 4 } and ${imgUrls[DEVICES[0 ]][sampleImg]} ` ) count = (count + 1 ) % 4 ; }, 64000 / 4 ); clearTimeout (imageChangeTimeOut); }, 2000 ); } }
这些代码能够按照约定更新图片,达到更多图片的轮播效果。虽然本文展示案例的时候只用了 个图片(有 个是上一篇固定的),但是使用本文的代码就能够实现更多的图片了。
没什么必要的其他东西 同时,在代码中我也留下了三个节点的console.log
方法,能够让各位能够感受到浏览器页面加载状态改变为complete
的时候、TimeOut
被触发的时候、Interval
被触发的时候。
如果不出意外的话,complete
输出将会很快出现,然后在 后出现timeout
,接下来每 产生changed
输出。
两篇文章的全部代码 如果你是单纯复制本篇文章中的内容,可能并不能运行起来。因为诸如DEVICES
、BASE_URL
这类变量是上一篇文章中定义的变量。
所以,在这里,我将给出两篇文章的全部代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 (function ( ) { const onPageLoaded = ( ) => document .dispatchEvent ( new Event ('page:loaded' , { bubbles : true }) ); if (document .readyState === 'loading' ) { document .addEventListener ('readystatechange' , onPageLoaded, { once : true }); addBackgroundImageDiv (); } else { onPageLoaded (); } document .addEventListener ('pjax:success' , onPageLoaded); })(); function addBackgroundImageDiv () { const opacityMask = document .createElement ("div" ); opacityMask.style .background = "linear-gradient(#fff, #ffced9, #fff)" ; opacityMask.style .position = "fixed" ; opacityMask.style .top = "0" ; opacityMask.style .left = "0" ; opacityMask.style .content = "" ; opacityMask.style .width = "100%" ; opacityMask.style .height = "100%" ; opacityMask.style .opacity = "0.8" ; opacityMask.style .zIndex = "-2" ; const imageContainer = document .createElement ("div" ); imageContainer.style .position = "fixed" ; imageContainer.style .top = "0" ; imageContainer.style .left = "0" ; imageContainer.style .content = "" ; imageContainer.style .width = "100%" ; imageContainer.style .height = "100%" ; imageContainer.style .zIndex = "-3" ; const imageScroller = document .createElement ("div" ); imageScroller.id = "image-scroller" ; imageScroller.style .position = "fixed" ; imageScroller.style .top = "0" ; imageScroller.style .left = "0" ; imageScroller.style .content = "" ; imageScroller.style .width = "400%" ; imageScroller.style .height = "100%" ; imageScroller.style .display = "flex" ; imageContainer.style .justifyContent = "space-around" ; imageContainer.style .alignContent = "center" ; imageContainer.style .alignItems = "center" ; imageScroller.style .zIndex = "-4" ; document .body .appendChild (opacityMask); document .body .appendChild (imageContainer); document .body .appendChild (imageScroller); const BASE_URL = 'http://images.sakebow.cn/bgimage/' const DEVICES = ['pc' ] const imgWindowUrl = { 'pc' : [ '/race-miku.jpg' , '/masuri-miku.jpg' , '/planet-miku.jpg' , '/4mikus.jpg' ] }; for (const imgUrlItem of imgWindowUrl['pc' ]) { const imageFrameItemContainer = document .createElement ("div" ); imageFrameItemContainer.style .width = imageContainer.style .width ; imageFrameItemContainer.style .height = "100%" ; imageFrameItemContainer.innerHTML = "<img" + " src='" + BASE_URL + DEVICES [0 ] + imgUrlItem + "'" + " style='width: 100%; height: 100%;'" + " alt='network broken?' />" ; imageScroller.appendChild (imageFrameItemContainer); } const imageRollStyle = document .createElement ('style' ); const EPOCH_TIME = 64 ; const ANIMATION_DEFAULT_SETTINGS = "s linear infinite running " ; imageRollStyle.innerHTML = `@keyframes image-roll { 0% { left: 0; } 24% { left: 0; } 25% { left: -100%; } 49% { left: -100%; } 50% { left: -200%; } 74% { left: -200%; } 75% { left: -300%; } 99% { left: -300%; } 100%{ left: 0; } }@keyframes image-translate-child-1 { 0% { scale: 1; opacity: 0 } 2% { scale: 1; opacity: 1; } 23% { scale: 1.1; } 25%, 100% { scale: 1.1; opacity: 0; } }#image-scroller>div:nth-child(1) { animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-1; }@keyframes image-translate-child-2 { 0%, 25% { scale: 1; opacity: 0 } 27% { scale: 1; opacity: 1; } 48% { scale: 1.1; } 50%, 100% { scale: 1.1; opacity: 0; } }#image-scroller>div:nth-child(2) { animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-2; }@keyframes image-translate-child-3 { 0%, 50% { scale: 1; opacity: 0 } 52% { scale: 1; opacity: 1; } 73% { scale: 1.1; } 75%, 100% { scale: 1.1; opacity: 0; } }#image-scroller>div:nth-child(3) { animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-3; }@keyframes image-translate-child-4 { 0%, 75% { scale: 1; opacity: 0 } 77% { scale: 1; opacity: 1; } 98% { scale: 1.1; } 100% { scale: 1.1; opacity: 0; } }#image-scroller>div:nth-child(4) { animation: ${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-translate-child-4; }` ; document .getElementsByTagName ('head' )[0 ].appendChild (imageRollStyle); imageScroller.style .animation = `${EPOCH_TIME + ANIMATION_DEFAULT_SETTINGS} image-roll` ; let count = 0 , index = 0 ; const imgUrls = { 'pc' : [ '/race-miku.jpg' , '/masuri-miku.jpg' , '/planet-miku.jpg' , '/4mikus.jpg' , '/84672028_p0.jpg' , '/84932457_p0.png' ] } document .onreadystatechange = function ( ) { if (document .readyState === 'complete' ) { console .log ('done' ); let imgChangeInterval = null ; let imageChangeTimeOut = setTimeout (function ( ) { console .log ('timeout' ); if (imgChangeInterval != null ) { clearInterval (imgChangeInterval); imgChangeInterval = null ; } imgChangeInterval = setInterval (function ( ) { index = (count + 2 ) % 4 const imageDivElement = document .getElementById ("image-scroller" ).children [count % 4 ]; let sampleImg = Math .floor (Math .random () * imgUrls[DEVICES [0 ]].length ); imageDivElement.innerHTML = "<img" + " src='" + BASE_URL + DEVICES [0 ] + imgUrls[DEVICES [0 ]][sampleImg] + "'" + " style='width: 100%; height: 100%;'" + " alt='network broken?' />" ; console .log (`changed, now is ${count % 4 } and ${imgUrls[DEVICES[0 ]][sampleImg]} ` ) count = (count + 1 ) % 4 ; }, 64000 / 4 ); clearTimeout (imageChangeTimeOut); }, 2000 ); } } }
到这里,动态修改、动态显示的背景图片就实现了。