前端實現旗幟飄動效果系列(Ⅰ):dom+css實現
hello,民娜桑~~我又來開新坑了( ̄ε(# ̄)╰╮o( ̄皿 ̄///),這次儘量保證把這個坑填完~
本系列我會分四篇來完成主題,分別是① DIV+CSS的實現,② canvas2D的簡單實現,③ canvas2D的進階實現,④ webgl+著色器的實現 以及 ⑤ 包裝成jquery外掛併發布為npm模組 。
這是整個系列完成以後的最終效果:
開始閱讀之前請確保您對高中的三角函式還有一定的印象以及瞭解基本的canvas繪圖操作——當然如果你確實不瞭解也沒事,這篇文章是使用div和css的實現,暫時沒有用到以上的知識。
首先講一下實現的原理,拿到一張圖片後,獲取其寬度,然後在效能允許的情況下,切成儘可能細的豎直切片,每個切片都用同一張背景圖片並將背景圖片的位置移動到切片的對應位置,然後通過css3關鍵幀動畫使切片元素以不同的時間軸來進行上下移動。很簡單是吧,如果你覺得so easy或者想根據原理自己試著實現一遍,那本文的後面你就可以直接跳過了。
html結構很簡單:
<!doctype html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>飄動的旗幟~</title>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
width: 100%;
background-color: lightgrey;
}
body {
text-align: center;
position: relative;
}
ul, li {
list-style: none;
}
#flag {
position: absolute;
left: 50%;
top: 50%;
}
/* 這裡是核心css樣式 */
</style>
</head>
<body>
<ul id="flag"></ul>
<script>
(function () {
// 這裡是js程式碼
})();
</script>
</body>
</html>
然後,準備一張圖片,比如這張艹貓路飛團的海盜旗,哎呀手滑,是草帽路飛團 (๑•̀ㅂ•́)و
接下來新增核心css程式碼:
/* 這裡是核心css樣式 */
#flag > li {
height: 100%;
float: left;
background-image: url("./img/flag.jpg");
background-size: auto 100%;
animation: flag ease-in-out infinite;
}
是的,你沒看錯,就是這麼點~~事實上並非如此,為了實現程式碼的靈活性,比如自定義週期數、週期長度、振幅、切片數量等,我使用js程式碼動態建立了style標籤,並將屬性計算後寫入。
下面是js程式碼,圖片地址我暫時是寫死的,通過上傳圖片自動生成動畫我會在最後一節封裝外掛時作為補充來說明。
// 這裡是js程式碼
var flagEle = document.getElementById(`flag`)
var image = new Image()
image.src = `./img/flag.jpg`
var IMG_MAX_WIDTH = 600
var IMG_MAX_HEIGHT = 600
var imgHeight
var imgWidth
image.onload = function () {
imgWidth = image.width
imgHeight = image.height
var ratio = image.width / image.height
if (imgWidth > IMG_MAX_WIDTH) {
imgWidth = IMG_MAX_WIDTH
imgHeight = imgWidth / ratio
}
if (imgHeight > IMG_MAX_HEIGHT) {
imgHeight = IMG_MAX_HEIGHT
imgWidth = imgHeight * ratio
}
flagEle.style.width = imgWidth + `px`
flagEle.style.height = imgHeight + `px`
flagEle.style.marginLeft = -imgWidth / 2 + `px`
flagEle.style.marginTop = -imgHeight / 2 + `px`
splitImg(100, 20, 2, 2)
}
雖然在圖片載入後有一堆程式碼,但是除了 splitImg(100, 20, 2, 2) ,事實上其他都無關緊要,前面那段程式碼的主要作用是定義一個容器的最大寬高,如果超過將會被等比例縮放。(不過並不推薦使用大圖,效能會是一個大問題)
下面使這段程式的核心方法——splitImg:
/**
* 分割圖片
* @param sliceCount 切片數量
* @param amplitude 振幅
* @param period 固定週期個數
* @param duration 一個週期的時長
*/
function splitImg (sliceCount, amplitude, period, duration) {
var styleEle = document.createElement(`style`)
// styleEle.innerHTML = `body{background: red}`
var styleHtmlAry = []
var sliceCountPerPeriod = Math.floor(sliceCount / period)
var sliceWidth = imgWidth / sliceCount
var formula = sliceCountPerPeriod + `n+`
var interval = duration * period / sliceCount
// 新增動畫延時
for (var i = 0; i < sliceCount; i++) {
if (i < sliceCountPerPeriod) {
styleHtmlAry.push(`#flag > li:nth-child(` + formula + i + `) { `)
styleHtmlAry.push(`animation-delay: -` + (interval * (sliceCountPerPeriod - i)) + `s;`)
styleHtmlAry.push(`}`)
}
styleHtmlAry.push(`#flag > li:nth-child(` + i + `) { background-position: -` + (i * sliceWidth) + `px 0; }`) // 設定切片背景
}
// 新增關鍵幀動畫
styleHtmlAry.push(`@keyframes flag {`)
styleHtmlAry.push(`0% { transform: translate3d(0, ` + amplitude + `px, 0); }`)
styleHtmlAry.push(`50% { transform: translate3d(0, -` + amplitude + `px, 0); }`)
styleHtmlAry.push(`100% { transform: translate3d(0, ` + amplitude + `px, 0); }`)
styleHtmlAry.push(`}`)
// 切片樣式
styleHtmlAry.push(`#flag > li {`)
styleHtmlAry.push(`animation-duration: ` + duration + `s;`) // 新增週期時長
styleHtmlAry.push(`width: ` + (imgWidth / sliceCount) + `px;`) // 設定切片寬度
styleHtmlAry.push(`}`)
styleEle.innerHTML = styleHtmlAry.join(``)
// 建立切片元素
flagEle.innerHTML = new Array(sliceCount + 1).join(`<li></li>`)
document.documentElement.appendChild(styleEle)
}
① 這裡的波形圖是使用的cos函式的表示形式,新增了三個關鍵幀:從波峰到波谷,再回到波峰。
② 因為使用了ease-in-out的動畫曲線,所以可以模擬出三角函式的波形圖
③ 原理和程式碼都比較簡單,可能比較需要注意的是這句 styleHtmlAry.push(`#flag > li:nth-child(` + formula + i + `) { `),對css3瞭解的朋友應該知道:nth-child的用法,括號裡面的是一個等差數列表示式,項數規定用n表示,那麼公差是多少呢,由於我們的動畫是週期性的,所以公差應該是每個週期包含的切片數量(正整數),即 var sliceCountPerPeriod = Math.floor(sliceCount / period)。
寫完以上程式碼,我們的基本雛形就出來了,這是切片數為80份,振幅20單位,2個週期,週期時長為2秒 時的效果圖:
是不是看著有點不對勁?旗子不是應該一邊固定的麼?怎麼兩邊一起動了?
辦法也簡單,只要我們在容器上加一個反方向的運動不就好了?
修改#flag樣式,新增如下樣式:animation: flag-reverse ease-in-out infinite;
#flag {
position: absolute;
left: 50%;
top: 50%;
transform: translate3d(-50%,-50%,0);
animation: flag-reverse ease-in-out infinite;
}
如下位置新增js程式碼:
// 新增關鍵幀動畫
...
// 新增反向關鍵幀動畫
styleHtmlAry.push(`@keyframes flag-reverse {`)
styleHtmlAry.push(`0% { transform: translate3d(0, ` + (-amplitude) + `px, 0); }`)
styleHtmlAry.push(`50% { transform: translate3d(0, ` + amplitude + `px, 0); }`)
styleHtmlAry.push(`100% { transform: translate3d(0, ` + (-amplitude) + `px, 0); }`)
styleHtmlAry.push(`}`)
// 容器應用flag-reverse動畫
styleHtmlAry.push(`#flag {`)
styleHtmlAry.push(`animation-duration: ` + duration + `s;`) // 新增週期時長
styleHtmlAry.push(`animation-delay: -` + (interval * sliceCountPerPeriod) + `s;`)
styleHtmlAry.push(`}`)
// 切片樣式
...
似乎沒問題了,看看效果:
納尼?怎麼兩邊都固定了?原來是因為我們指定2個週期,只要不是週期的整數倍就行了,在原來的基礎上改為1.5個週期試試:
到這裡我們的dom+css的實現方式就結束啦,這種方式的優點很明顯,就是實現簡單;缺點也不少,比如無法新增高光效果,整體振幅一致不符合常理,切片過多容易造成的頁面阻塞與記憶體洩露,下一節 我會用canvas2D畫素級的操作實現該效果,可以很大程度上避免這些問題。
Demo:See the Pen flag waving by dom+css by Kay (@oj8kay) on CodePen.
原文釋出時間為:2018年07月03日
原文作者:oj8kay
本文來源:開源中國 如需轉載請聯絡原作者
相關文章
- canvas實現的雪花飄落效果Canvas
- 前端實現文字滾動效果前端
- canvas實現的雪花飄落效果程式碼例項Canvas
- 詳細解析:uni-app|vue元件實現會撒嬌的旗幟徽章純CSS動效APPVue元件CSS
- Android自定義View——從零開始實現雪花飄落效果AndroidView
- 紅旗飄飄——中線操作技巧之七,什麼是紅旗飄飄
- canvas系列三之《煙花》效果實現Canvas
- javascript實現滑鼠懸浮圖片實現抖動效果JavaScript
- 部落格園美化-隨季節變化實現不同的飄落效果
- [前端外掛] js返回頂部 效果實現前端JS
- 前端動畫效果實現的簡單比較前端動畫
- SVG實現動態模糊動畫效果SVG動畫
- SVG 實現動態模糊動畫效果SVG動畫
- css實現視差滾動效果CSS
- CSS題目系列(3)- 實現文字切割效果CSS
- 實現聚焦效果
- 實現前端彈簧動效前端
- 【前端】javascript實現導航欄筋斗雲效果特效前端JavaScript特效
- 巢狀滾動效果實現討論巢狀
- jquery實現滑動門效果詳解jQuery
- svg實現矩形水平運動動畫效果SVG動畫
- Delphi中TFlowPanel實現滾動條效果
- 實現爆炸後的振動效果 (轉)
- 實現抖音 “影片無限滑動“效果
- Lottie-前端實現AE動效前端
- 使用gulp實現前端自動化前端
- webgl實現故障效果Web
- webgl實現火焰效果Web
- css 實現打分效果CSS
- js實現打字效果JS
- canvas實現波浪效果Canvas
- React實現動畫效果React動畫
- iOS全景效果實現iOS
- Javascript實現動畫效果JavaScript動畫
- 如何實現倒影效果
- 前端基礎入門五(掌握jQuery的常用api,實現動態效果)前端jQueryAPI
- 前端實現列印前端
- 微信小程式實現卡片左右滑動效果微信小程式