素のJavaScript で Canvas アニメーションを実装する方法
2019-06-28
目次
完成品
See the Pen LKOoJG by daichi (@da10410) on CodePen.
よくヘッダーなどで目にするこのアニメーション。canvas と javascript でちゃちゃっと作ってみました!
Step1
まず、canvas タグを設置します。この時点では何も出てきません。
<div class="canvas-box">
<p>CANVAS 入門</p>
<canvas id="canvas01"></canvas>
</div>
Step2
次に下記の様な js を書くと、丸が 20 個ランダムな位置、ランダムなサイズで描画されま す!
$(function () {
// キャンバスサイズ
const WIDTH = 500
const HEIGHT = 200
// canvas要素取得
const canvas01 = $("#canvas01").get(0)
canvas01.width = WIDTH
canvas01.height = HEIGHT
// コンテキスト取得
const ctx = canvas01.getContext("2d")
// 丸の数
const circleNum = 20
// 丸の最大最小サイズ
const circleMinSize = 2
const circleMaxSize = 5 - circleMinSize
// 円の情報
const circles = []
// 円の追加
for (var i = 0; i <= circleNum; i++) {
let xpath = Math.floor(Math.random() * WIDTH + 1)
let ypath = Math.floor(Math.random() * HEIGHT + 1)
const size = Math.floor(Math.random() * circleMaxSize + 1) + circleMinSize
const xspeed = Math.round(Math.random() * 20 - 10, 10) / 10
const yspeed = Math.round(Math.random() * 20 - 10, 10) / 10
circles.push({ x: xpath, y: ypath, r: size, xs: xspeed, ys: yspeed })
}
// 各円を描画
for (const c of circles) {
ctx.beginPath()
ctx.fillStyle = "rgb(0, 0, 0)" // 黒色
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI)
ctx.fill()
}
})
上記の内容を解説していきます。
まず、canvas 要素を js(今回は jQuery)で取得し、サイズを設定します。
// キャンバスサイズ
const WIDTH = 500
const HEIGHT = 200
// canvas要素取得
const canvas01 = $("#canvas01").get(0)
canvas01.width = WIDTH
canvas01.height = HEIGHT
コンテキストを取得します。
const ctx = canvas01.getContext("2d")
円の各情報を作成します。 各変数は下記の様な意味になっています。
| 変数名 | 内容 | | ------ | -------------------------- | | xpath | top からの位置 | | ypath | left からの位置 | | size | 円のサイズ | | xspeed | 1 描画毎に縦に移動する長さ | | xspeed | 1 描画毎に横に移動する長さ |
// 円の情報
const circles = []
// 円の追加
for (var i = 0; i <= circleNum; i++) {
let xpath = Math.floor(Math.random() * WIDTH + 1)
let ypath = Math.floor(Math.random() * HEIGHT + 1)
const size = Math.floor(Math.random() * circleMaxSize + 1) + circleMinSize
const xspeed = Math.round(Math.random() * 20 - 10, 10) / 10
const yspeed = Math.round(Math.random() * 20 - 10, 10) / 10
circles.push({ x: xpath, y: ypath, r: size, xs: xspeed, ys: yspeed })
}
最後に円の情報が入っている配列分だけループで回し描画を行います。
// 各円を描画
for (const c of circles) {
ctx.beginPath()
ctx.fillStyle = "rgb(0, 0, 0)" // 黒色
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI)
ctx.fill()
}
Step3
これだけでは描画静止画になってしまいますので、ループで描画を回し動きを付けます。
$(function () {
// キャンバスサイズ
const WIDTH = 500
const HEIGHT = 200
// 丸の数
const circleNum = 20
// 丸の最大最小サイズ
const circleMinSize = 2
const circleMaxSize = 5 - circleMinSize
// canvas要素取得
const canvas01 = $("#canvas01").get(0)
canvas01.width = WIDTH
canvas01.height = HEIGHT
// コンテキスト取得
const ctx = canvas01.getContext("2d")
// 円の情報
const circles = []
// 円の追加
for (var i = 0; i <= circleNum; i++) {
let xpath = Math.floor(Math.random() * WIDTH + 1)
let ypath = Math.floor(Math.random() * HEIGHT + 1)
const size = Math.floor(Math.random() * circleMaxSize + 1) + circleMinSize
const xspeed = Math.round(Math.random() * 20 - 10, 10) / 10
const yspeed = Math.round(Math.random() * 20 - 10, 10) / 10
circles.push({ x: xpath, y: ypath, r: size, xs: xspeed, ys: yspeed })
}
// 各円を描画
for (const c of circles) {
ctx.beginPath()
ctx.fillStyle = "rgb(0, 0, 0)" // 黒色
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI)
ctx.fill()
}
function loop(ts) {
// 画面をクリア
ctx.clearRect(0, 0, WIDTH, HEIGHT)
// 塗りつぶし
ctx.fillStyle = "#48dbfb"
ctx.fillRect(0, 0, WIDTH, HEIGHT)
let num = 0
// 各円を描画する。
for (const c of circles) {
let afters = circleNum - num
if (c.y <= 0 || c.y >= HEIGHT) {
c.ys = c.ys * -1
}
if (c.x <= 0 || c.x >= WIDTH) {
c.xs = c.xs * -1
}
// 線引く
for (let n = 0; n <= afters; n++) {
if (
Math.sqrt(
(c.x - circles[circleNum - (afters - n)].x) ** 2 +
(c.y - circles[circleNum - (afters - n)].y) ** 2
) <= 70
) {
ctx.beginPath()
ctx.strokeStyle = "#fff"
ctx.moveTo(c.x, c.y)
ctx.lineTo(
circles[circleNum - (afters - n)].x,
circles[circleNum - (afters - n)].y
)
ctx.ineWidth = 0.1
ctx.closePath()
ctx.stroke()
}
}
c.x = c.x + c.xs
c.y = c.y + c.ys
ctx.beginPath()
ctx.fillStyle = "#fff"
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI)
ctx.fill()
}
num++
window.requestAnimationFrame(ts => loop(ts))
}
window.requestAnimationFrame(ts => loop(ts))
})
まず、ループする関数を作成します。
function loop(ts) {
// 画面をクリア
ctx.clearRect(0, 0, WIDTH, HEIGHT)
// 塗りつぶし
ctx.fillStyle = "#48dbfb"
ctx.fillRect(0, 0, WIDTH, HEIGHT)
let num = 0
// 各円を描画する。
for (const c of circles) {
let afters = circleNum - num
if (c.y <= 0 || c.y >= HEIGHT) {
c.ys = c.ys * -1
}
if (c.x <= 0 || c.x >= WIDTH) {
c.xs = c.xs * -1
}
// 線引く
for (let n = 0; n <= afters; n++) {
if (
Math.sqrt(
(c.x - circles[circleNum - (afters - n)].x) ** 2 +
(c.y - circles[circleNum - (afters - n)].y) ** 2
) <= 70
) {
ctx.beginPath()
ctx.strokeStyle = "#fff"
ctx.moveTo(c.x, c.y)
ctx.lineTo(
circles[circleNum - (afters - n)].x,
circles[circleNum - (afters - n)].y
)
ctx.ineWidth = 0.1
ctx.closePath()
ctx.stroke()
}
}
c.x = c.x + c.xs
c.y = c.y + c.ys
ctx.beginPath()
ctx.fillStyle = "#fff"
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI)
ctx.fill()
}
num++
window.requestAnimationFrame(ts => loop(ts))
}
canvas でのアニメーションはパラパラ漫画の様に、一度書いて、消して、書いて~…を繰り返しています。 下記でループ毎に画面を一度クリアし、水色で全面塗りつぶしを行っています。
// 画面をクリア
ctx.clearRect(0, 0, WIDTH, HEIGHT)
// 塗りつぶし
ctx.fillStyle = "#48dbfb"
ctx.fillRect(0, 0, WIDTH, HEIGHT)
下記ではそれぞれの丸の位置が、canvas の枠を飛び出しそうになった時に 方向転換をするための内容となっています。 ここで丸のサイズを取得し、その半径を現在位置を考慮して計算を行えば 丸が枠から少し見切れることも無くなります。
if (c.y <= 0 || c.y >= HEIGHT) {
c.ys = c.ys * -1
}
if (c.x <= 0 || c.x >= WIDTH) {
c.xs = c.xs * -1
}
下記では丸と丸の間を結ぶ線を描画しています。 三平方の定理を使用して、距離が 70px より近くなった場合に線を引く実装になっています。
// 線引く
for (let n = 0; n <= afters; n++) {
if (
Math.sqrt(
(c.x - circles[circleNum - (afters - n)].x) ** 2 +
(c.y - circles[circleNum - (afters - n)].y) ** 2
) <= 70
) {
ctx.beginPath()
ctx.strokeStyle = "#fff"
ctx.moveTo(c.x, c.y)
ctx.lineTo(
circles[circleNum - (afters - n)].x,
circles[circleNum - (afters - n)].y
)
ctx.ineWidth = 0.1
ctx.closePath()
ctx.stroke()
}
}
最後に下記で丸の現在位置を更新する処理を行い、丸を描画しています。
c.x = c.x + c.xs
c.y = c.y + c.ys
ctx.beginPath()
ctx.fillStyle = "#fff"
ctx.arc(c.x, c.y, c.r, 0, 2 * Math.PI)
ctx.fill()
下記でループする関数を呼び出しています。
window.requestAnimationFrame(ts => loop(ts))
終わりに
アニメーションは難しく感じる事が多いですが、Canvas のアニメーションは触ってみると案外単純です。 義務教育レベルの数学と javascript の知識さえあれば、簡単なアニメーションをすぐに作成する事ができます。 是非皆さんも試してみてください!
※計算式などいついて後々追記していけたらと考えています :thinking: