canvas實現HTML5「正義聯盟要造反」小動畫




其實結構不復雜,主要分為五塊:

1、文字區 .story

2、canvas 畫布 .main

3、組件區 .package

4、分享區 .share-layer

5、背景音樂 #bg-music

配置文件

動畫分為四個場景,以正義聯盟的四個英雄為主角:蝙蝠俠、超人、神奇女俠、閃電俠。

在 config.js 文件中定義好每個場景故事內容和角色對象。

var stories= [{
title:'老闆:蝠蝠,加個班吧!',
content:'蝙蝠俠:不加,老子有錢任性。'
}, {
title:'老闆:超超,加個班吧!',
content:'超人:你被炒了,現在我是老闆。'
}, {
title:'老闆:美奇,加個班吧!',
content:'神奇女俠:我加班的小心心留給你。'
}, {
title:'老闆:小電,加個班吧!',
content:'閃電俠:老闆,再見!'
}, {

}];
//繪製蝙蝠俠對象
var batmanImg = new Image();
batmanImg.src = 'img/batman.png';
var batman = {
x: 50,
y: 500,
vy: 4,
w: 75,
h: 100,
au: 'audio/fall.mp3',
direction: 'start'
}
//繪製老闆對象
var bossImg = new Image();
bossImg.src = 'img/boss.png';
var boss = {
x: 240,
y: 500,
vy: 4,
w: 75,
h: 100,
direction: 'start'
}
//更多角色代碼在此省略......

場景一:蝙蝠俠

canvas實現HTML5“正義聯盟要造反”小動畫

蝙蝠俠場景演示

先來說一下打字機效果。

$.fn.autotype = function() {}; 可以理解為為 jquery 對象定義了一個 autotype 方法。

//打字效果方法
$.fn.autotype = function() {
var $text = $(this);
console.log('this', this);
var str = $text.html(); //返回被選 元素的內容
var index = 0;
var x = $text.html('');
//$text.html()和$(this).html('')有區別
var timer = setInterval(function() {
//substr(index, 1) 方法在字符串中抽取從index下標開始的一個的字符
var current = str.substr(index, 1);
if (current == ' //indexOf() 方法返回">"在字符串中首次出現的位置。
index = str.indexOf('>', index) + 1;
} else {
index++;
}
//console.log(["0到index下標下的字符",str.substring(0, index)],["符號",index & 1 ? '_': '']);
//substring() 方法用於提取字符串中介於兩個指定下標之間的字符
$text.html(str.substring(0, index) + (index & 1 ? '_': ''));
if (index >= str.length) {
clearInterval(timer);
}
},
100);
};

然後定義一個 autotypeinit 函數,先清空 .story-title 和 .story-content 中的文字,然後調用 .autotype() 方法產生打字效果。這裡 setTimeout 函數是為了先把第一句話打完,再打第二句話。

//調用打字方法
function autotypeinit(i){
$('.story-content').html('');
$('.story-title').html('');
$('.story-title').html(stories[i].title);
$('.story-title').autotype();
setTimeout(function(){
$('.story-content').html(stories[i].content);
$('.story-content').autotype();
},1000);
}

canvas 實現動畫的原理就是不斷繪製圖形、改變圖形(形狀、位移等)、清除畫布的過程。

canvas實現HTML5“正義聯盟要造反”小動畫

動畫循環過程

ctx.clearRect(0, 0, canvas.width, canvas.height) 清除畫布,會將整個畫布的內容清除。

requestAnimationFrame() 相當於一個回調函數,不斷調用函數自身就可以實現循環。

蝙蝠俠動畫流程:

canvas實現HTML5“正義聯盟要造反”小動畫

蝙蝠俠流程圖

1、先進行碰撞檢測,寶箱底部(chest.y + chest.h)是否等於 BOSS 頂部 (boss.y初始值,後會變),代碼中 425 = chest.h + boss.y。如果不等就下落(chest.y += chest.vy)。

2、碰撞開始,寶箱依然要下落(chest.y += chest.vy),BOSS高度變小(boss.h -= boss.vy)。為了保持寶箱下落速度和BOSS變形速度一致,boss.vy == chest.vy。還有最重要的一點 boss.y 也要以相同的速度下降(boss.y += boss.vy),否則變成 BOSS 腳往上縮小。

3、蝙蝠俠在檢測到寶箱壓扁BOSS後,即BOSS高度變為某最小值(boss.h == 設定值),開始上升(batman.y -= batman.vy)直到移出屏幕(batman.y == -batman.h)。

//蝙蝠俠場景
function dropBoss(){
console.log('chest.y',chest.y);
// 寶箱下落
if (chest.y < 425) {
chest.y += chest.vy;
}else{
if(boss.h > 25){
boss.h -= boss.vy;
boss.y += boss.vy;
chest.y += chest.vy;
}else{
if(batman.y > -100){
batman.y -= batman.vy;
}else{
count = 1;
autotypeinit(count);
chgBoss();
return;
}
}
}
// 清除畫布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重繪
ctx.drawImage(batmanImg,batman.x,batman.y,batman.w,batman.h);
ctx.drawImage(bossImg,boss.x,boss.y,boss.w,boss.h);
ctx.drawImage(chestImg,chest.x,chest.y,chest.w,chest.h);
// 使用requestAnimationFrame實現動畫循環

requestAnimationFrame(dropBoss);
}

場景二、超人

canvas實現HTML5“正義聯盟要造反”小動畫

超人場景演示

超人動畫流程:

canvas實現HTML5“正義聯盟要造反”小動畫

超人流程圖

超人動畫比較簡單,JavaScript控制部分不多,一些動畫藉助了 CSS3 。

1、顯示 BOSS($('.boss').show();),墨鏡落下(sunglass.y += sunglass.vy)。

2、當眼鏡戴在超人身上後(sunglass.y == 520,這個值是通過觀察畫面得出的,畢竟有範兒的墨鏡要戴對位置嘛),BOSS 隱藏,員工出現,問號出現($('.boss').hide(); $('.staff').show(); $('.question').show(); )。setTimeout 在這裡設置 2s 是為了讓員工和問號的動畫顯示完成,而不是墨鏡一戴好後就切換場景。

//超人場景
function chgBoss(){
console.log('sunglass.y',sunglass.y);
$('.boss').show();
// 移動墨鏡
if (sunglass.y < 520) {
sunglass.y += sunglass.vy;
}else{
$('.boss').hide();
$('.staff').show();
$('.question').show();
count = 2;
setTimeout(function(){
autotypeinit(count);
koBoss();
},2000);
return;
}
// 清除畫布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重繪
ctx.drawImage(supermanImg,superman.x,superman.y,superman.w,superman.h);
ctx.drawImage(sunglassImg,sunglass.x,sunglass.y,sunglass.w,sunglass.h);
// 使用requestAnimationFrame實現動畫循環
requestAnimationFrame(chgBoss);
}

CSS 部分

  • staff 和 question 動畫部分是由 CSS 完成的。staff、question 先在對應位置定位好。
  • staff 右旋消失,持續 1s ,延遲 1s ,只執行一次。
  • question 閃爍,持續 2s ,無限循環。
/*角色*/
.boss{
display: none;
position: absolute;
top: 500px;
left: 240px;
z-index:1;
}
.staff{
display: none;
position: absolute;
top: 500px;
left: 240px;
z-index: 10;
-webkit-animation: rotateOutUpRight 1s 1s 1;
animation: rotateOutUpRight 1s 1s 1;
}
.question{
display: none;
position: absolute;
top: 450px;
left: 270px;
z-index: 10;
-webkit-animation: flash 2s infinite;
animation: flash 2s infinite;
}
@-webkit-keyframes rotateOutUpRight{
0%{
transform-origin:right bottom;
opacity:1
}

0%,to{
-webkit-transform-origin:right bottom
}
to{
transform-origin:right bottom;
-webkit-transform:rotate(90deg);
transform:rotate(90deg);
opacity:0
}
}
@keyframes rotateOutUpRight{
0%{
transform-origin:right bottom;
opacity:1
}
0%,to{
-webkit-transform-origin:right bottom
}
to{
transform-origin:right bottom;
-webkit-transform:rotate(90deg);
transform:rotate(90deg);
opacity:0
}
}
@-webkit-keyframes flash{
0%,50%,to{
opacity:1;
}
25%,75%{
opacity:0;
}
}
@keyframes flash{
0%,50%,to{
opacity:1;
}
25%,75%{
opacity:0;
}
}

為什麼有些組件使用 drawImage 而有些使用 CSS3 搭配 show / hide 控制呢?

因為 superman 和 sunglass 是一直出現在畫面中的,而且只涉及到位移操作,那麼使用 drawImage 不斷迭代是比較方便的,這樣在切換下一個場景的時候組件也被自動清除。

而 boss 、staff 、question 只在特定情況下會顯示或隱藏,右旋消失和閃爍動畫使用 CSS3 比較好實現。

canvas 的位移、旋轉等操作是針對整個畫布來變化的,可以試試以下代碼,會產生神奇的效果~

//平移物體至原點位置 
ctx.translate(300,200);
//以新原點為中心旋轉60°
ctx.rotate(Math.PI / 3);

場景三、神奇女俠

canvas實現HTML5“正義聯盟要造反”小動畫

神奇女俠場景演示

神奇女俠動畫流程:

canvas實現HTML5“正義聯盟要造反”小動畫

神奇女俠流程圖

神奇女俠動畫就更簡單了,同樣藉助了 CSS3 。

1、先隱藏上一場景的 staff 和 question。顯示 BOSS($('.boss').show(),這裡使用 show 的原因是 BOSS 沒有動畫,下一場景也有出現

)。

2、神奇女俠左移(wonderwoman.x -= wonderwoman.vy),小心心顯示($('.heart').show()),直到神奇女俠移出屏幕(wonderwoman.x == -wonderwoman.w)。

//神奇女俠場景
function koBoss(){
console.log('wonderwoman.y',wonderwoman.y);
$('.boss').show();
$('.staff').hide();
$('.question').hide();
//移動小人
if (wonderwoman.x > -75) {
wonderwoman.x -= wonderwoman.vy;
$('.heart').show();
}else{
count = 3;
autotypeinit(count);
flashBoss();
return;
}
// 清除畫布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重繪
ctx.drawImage(wonderwomanImg,wonderwoman.x,wonderwoman.y,wonderwoman.w,wonderwoman.h);
// 使用requestAnimationFrame實現動畫循環
requestAnimationFrame(koBoss);
}

CSS 部分

  • heart 右旋,持續 4.5s , 延遲 1s ,執行一次。
.heart{
display: none;
position: absolute;
top: 550px;
left: 100px;
z-index: 10;

-webkit-animation: rotate 4.5s 1s 1;
animation: rotate 4.5s 1s 1;
}
@-webkit-keyframes rotate{
0%{
transform-origin:right bottom;
}
0%,to{
-webkit-transform-origin:right bottom
}
to{
transform-origin:right bottom;
-webkit-transform:rotate(90deg);
transform:rotate(90deg);
}
}
@keyframes rotate{
0%{
transform-origin:right bottom;
}
0%,to{
-webkit-transform-origin:right bottom
}
to{
transform-origin:right bottom;
-webkit-transform:rotate(90deg);
transform:rotate(90deg);
}
}

場景四、閃電俠

canvas實現HTML5“正義聯盟要造反”小動畫

閃電俠場景演示

閃電俠動畫流程:

canvas實現HTML5“正義聯盟要造反”小動畫

閃電俠流程圖

1、為了體現閃電俠來無影去無蹤的效果(簡稱多動症),閃電俠在邊上升(flash.y -= flash.vy)的同時邊在屏幕左右側隨機移動,使用了隨機函數 Math.random() 為 flash.x 在 (min,max) 範圍內隨機賦值。

2、電骷髏疊加在 BOSS 前的快速閃現營造 BOSS 被電擊的感覺。

3、因為是最後一個場景了,因此只需要顯示分享按鈕, return 跳出函數即可。

//閃電俠場景
function flashBoss(){
console.log('flash.y',flash.y);
$('.heart').hide();
if (flash.y > -100) {
flash.y -= flash.vy;
flash.x = parseInt(Math.random()*(max-min+1)+min,10);
$('.skull').show();
}else{
$('.over').show();
return;
}
// 清除畫布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重繪
ctx.drawImage(flashImg,flash.x,flash.y,flash.w,flash.h);
// 使用requestAnimationFrame實現動畫循環
requestAnimationFrame(flashBoss);
}

CSS 部分

  • skull 閃爍,持續 0.5s,無限循環。
.skull{
display: none;
position: absolute;
top: 475px;
left: 220px;
z-index: 10;
-webkit-animation: flash 0.5s infinite;
animation: flash 0.5s infinite;
}

背景音樂

本地開發的時候,audio 標籤即使加了 autoplay 屬性,也依然不能自動播放。如果用戶還沒進行交互就調用播放聲音的 API ,Chrome 會這麼提示:

DOMException: play() failed because the user didn't interact with the document first.

因此增加了一個開始按鈕,audio.play() 放在按鈕的點擊事件裡來觸發聲音播放。

但是當代碼傳上服務器後,iOS 下手機QQ自帶的瀏覽器和 Safari、PC端 Chrome 都可以自動播放,微信瀏覽器需要點擊後播放。估計是不同廠商瀏覽器做了不同的限制。

不足和可優化的地方

1、項目沒有考慮響應式。

2、代碼有些冗餘,工程化思維欠缺。

3、蝙蝠俠的上升速度可以先慢後快,可以使用勻變速直線運動或者嘗試緩動函數 ease。

canvas實現HTML5“正義聯盟要造反”小動畫

勻變速直線運動公式

4、場景一可以增加滿屏金幣掉落效果。


分享到:


相關文章: