效果展現css
githubhtml
春節放假前的次日上午,看到設計稿上的圖(以下),因而發生了下面的對話git
-我:把這個圖傳一下吧。github
-UI:好的,你是要 gif 吧。typescript
-我:啥?這個是個動畫?canvas
-UI:啥?這不是動畫?函數
-我:打擾了。oop
因而在剩下的一天裏,我擼出了這個波浪動畫動畫
首先咱們能想到的是,波浪的數量必定是可定製的,其次是顏色,速度,透明度,高度等等。這些可定製的參數應當在函數實例化時傳入。因此咱們先定製一個接口(包括了後來在編寫時我認爲須要的)。ui
interface Options {
number: number
smooth: number
velocity: number
height: number
colors: Array<{ hex: string, rgba: string }>
opacity: number
border: {
show: boolean,
width: number,
color: string[]
}
position: 'top' | 'bottom' | 'left' | 'right'
}
複製代碼
不瞭解 typescript 的同窗能夠意會,key
指的傳入變量的名稱,value
爲變量的類型規範
咱們先預先想下可能發生的場景:
因此咱們暫時值規定四個類型爲 public
接口 animate
,pause
,setOptions
,reset
interface Core {
animate: () => void
pause: () => void
setOptions: (options: Object) => void
reset: () => void
}
複製代碼
準備工做基本完成,如今開始編寫核心動畫。
基本操做,很少介紹。
<body>
<canvas id="canvas"></canvas>
</body>
複製代碼
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = document.body.offsetWidth
canvas.height = document.body.offsetHeight
複製代碼
畫一個矩形,而後填充顏色。
ctx.fillStyle = "rgba(255,118,87,.6)"
ctx.beginPath()
ctx.moveTo(0, canvas.height/2)
ctx.lineTo(canvas.width, canvas.height/2)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, canvas.height/2)
ctx.closePath()
ctx.fill()
複製代碼
要讓水池的水呈週期性的上漲和跌落,很容易想到藉助正弦或者餘弦函數來完成,在每一幀的渲染中將 step
增長 1 度,規定 50 爲變化值,利用正弦函數將變化量做用於左右兩個頂點上便可。
let step = 0
function loop(){
// 清空canvas
ctx.clearRect(0,0,canvas.width,canvas.height)
ctx.clearRect(0,0,canvas.width,canvas.height)
ctx.fillStyle = "rgba(255,118,87,.6)"
step++
const angle = step * Math.PI / 180
const deltaHeight = Math.sin(angle) * 50
ctx.beginPath()
ctx.moveTo(0, canvas.height/2+deltaHeight)
ctx.lineTo(canvas.width, canvas.height/2+deltaHeight)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, canvas.height/2+deltaHeight)
ctx.closePath()
ctx.fill()
requestAnimationFrame(loop)
}
loop()
複製代碼
使左右頂點不一樣步便可讓水面晃動起來,因此咱們將左頂點的取值使用餘弦函數便可。
let step = 0
function loop() {
//清空canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "rgba(255,118,87,.6)"
step++
const angle = step * Math.PI / 180
const deltaHeight = Math.sin(angle) * 50
const deltaHeightRight = Math.cos(angle) * 50
ctx.beginPath()
ctx.moveTo(0, canvas.height / 2 + deltaHeight)
ctx.lineTo(canvas.width, canvas.height / 2 + deltaHeightRight)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, canvas.height / 2 + deltaHeight)
ctx.closePath()
ctx.fill()
requestAnimationFrame(loop)
}
loop()
複製代碼
藉助貝塞爾曲線將矩形的一邊變爲波浪。
在 canvas 繪製中,咱們藉助 bezierCurveTo(cpX1, cpY1, cpX2, cpY2, x, y)
方法繪製貝塞爾曲線,由上圖可見(歪脖 45 度查看更直觀),兩個控制點的橫座標應當設爲矩形寬的中點,縱座標跟隨峯值變化便可。
let step = 0
function loop() {
//清空canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "rgba(255,118,87,.6)"
step++
const angle = step * Math.PI / 180
const deltaHeight = Math.sin(angle) * 50
const deltaHeightRight = Math.cos(angle) * 50
ctx.beginPath()
ctx.moveTo(0, canvas.height/2+deltaHeight)
ctx.bezierCurveTo(canvas.width /2, canvas.height/2+deltaHeight-50, canvas.width / 2, canvas.height/2+deltaHeightRight-50, canvas.width, canvas.height/2+deltaHeightRight)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, canvas.height/2+deltaHeight)
ctx.closePath()
ctx.fill()
requestAnimationFrame(loop)
}
loop()
複製代碼
將上面的波浪寫成 for 循環屢次渲染便可。
let step = 0
const lines = 3
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
step++
for (let i = 0; i < lines; i++) {
ctx.fillStyle = 'rgba(255,118,87,.3)'
var angle = (step + i * 180 / lines) * Math.PI / 180
var deltaHeight = Math.sin(angle) * 50
var deltaHeightRight = Math.cos(angle) * 50
ctx.beginPath()
ctx.moveTo(0, canvas.height / 2 + deltaHeight)
ctx.bezierCurveTo(canvas.width / 2, canvas.height / 2 + deltaHeight - 50, canvas.width / 2, canvas.height / 2 + deltaHeightRight - 50, canvas.width, canvas.height / 2 + deltaHeightRight)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, canvas.height / 2 + deltaHeight)
ctx.closePath()
ctx.fill()
}
requestAnimationFrame(loop)
}
loop()
複製代碼
核心代碼已經敘述完畢,其他針對設置項的封裝就再也不贅述了(基本是將上述的核心部分進行變量化設置)。
若是你也有這樣的需求,能夠參考上述示例簡單使用,也可使用我編寫的
最後的最後,歡迎 star ~