#22-掰惹Gif!用Sprite雪碧图做动画! (CSS & Canvas)

有时候会碰到网站要放GIF动画,但GIF大小动辄几M起跳,
造成网页Loading慢、图片边缘锯齿,支援颜色也不多,不太完美。

这时候 sprite 雪碧图就登场啦~
其实雪碧图就是将动画拆解成一张一张图片,
再用CSS animation steps的做法(就跟之前换字特效是一样的!)
让他可以一张一张播放。

当然雪碧图也可以用在组合多张图片,让网路只要载入一次,
不过这个应用就不会在这边赘述罗~(我也还不太会

今天就来用

1.CSS Animation
2.Canvas + JS

两种方法来做sprite动画!
当然这边就要 设计师请支援动画
但我一个人兼撞钟,眼睛就忍耐一下!


1. CSS Animation

很粗糙地先做了五个连续图片~让云和灯光可以动起来。

直觉可能会想到要一张图片一张图片换,
但这样做会造成缺点:多个HTTPS的请求、会造成档案闪烁不利於管理等等。

雪碧图搭配移动图片位置的方法换图片,
只要一张图片搞定啦!

来看成品:

直接上code:

//css
.hero {
            width: 648px;
            height: 572px;
            background: url(./city-sprite.png);
            background-size: auto 100%;
            animation: bg steps(4) 5s infinite;//移动4次
        }

        @keyframes bg {
            to {
                background-position: -2592px 0; //算好移动到最後一张的宽度
            }

        }

动态成果这边请


2.Canvas + JS

Canvas的话,就是不断擦掉画面,
requestAnimationFrame(animate);
不断呼叫自己重绘图片到Canvas上~

上图片~

我们一样要算出每格的距离,然後让Canvs去吃那格的座标~
来看看成果~

直接看code说明吧~

//JS
//canvas 起手式
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 600;

//canvas 放图片起手式
const spriteSheet = new Image();
spriteSheet.src="./test.png";

//异步操作,让图片载入後才执行
spriteSheet.onload = loadImage;

//spriteSheet的宽高除以图片有几格,就是一格的宽度和高度
//这里有五格,但只有一行
let cols = 5;
let rows = 1;
let spriteWidth = 1147 / cols; //图片宽度除格数
let spriteHeight = 557 / rows; //图片高度除格数

//图片切割的起始点
let srcX =0;
let srcY =0;

//这边是控制速度用的~
let totalFrames = 5;
let currentFrame = 0;
let frameDrawn =0;

function animate(){
  //先清掉画布
  ctx.clearRect(0,0,canvas.width, canvas.height);
  
  
  //这边用於数的概念来看现在是第几格了~等到第五格上限时,currentFrame又回从一开始~
  currentFrame = currentFrame % totalFrames;
  
  //现在这个是第几格 
  srcX = currentFrame * spriteWidth;
  
	//画画罗参数分别是:1. 读取图片 2.图片要切割的x位置 3.切割的y位置
	//4.切割多宽 5.切割多长 6.要放在canvas的x位置 7.要放在canvas的y位置
	//8.canvas贴上的地方要放多宽 9.canvas贴上的地方要要放多长
  ctx.drawImage(spriteSheet, srcX, srcY,spriteWidth, spriteHeight,
 0,0 ,spriteWidth, spriteHeight);
  
  //就是这边控制速度
  //等於是这个animate被呼叫10次以後,currentFrame才会前进一格,以控制速度
  frameDrawn++;
  if(frameDrawn>=10){
    currentFrame++;
    frameDrawn=0;
  }

  //重复呼叫自己
  requestAnimationFrame(animate);
}

function loadImage(){
  animate();
}

// ref: https://www.youtube.com/watch?v=MHGgVlrlkYc&t=907s

成果看这边


以上!

完成了我们的小人物动画罗!
明天就来利用这个小人物来做互动小游戏~(命名:都市老妹生存记
敬请期待 XD
Canvas的sprite我主要是参考这个影片

有错或是有任何建议欢迎批评指教!


参考文章
帧动画的多种实现方式与效能对比(推荐阅读!)
https://www.gushiciku.cn/pl/2RQ8/zh-tw

CSS3动画之逐帧动画
https://jelly.jd.com/article/6006b1035b6c6a01506c87a7


<<:  Day24_CSS语法7

>>:  不只懂 Vue 语法:试说明 Composition API 与 Options API 概念和语法的分别?

D20/ 怎麽在 compose 与 non-compoe 间传资料 - Compose Side-Effect part 2

今天大概会聊到的范围 rememberUpdateState 上一篇聊到,SideEffect 周...

高档爆大量,请提高警觉

在入场,且获利後,第一件事就是要保护获利,而不是不要赔钱。 通常有几个转折点需要注意,其中高档爆大量...

Day 10:v-for 注定绑个 key

承上篇,谈到 v-for,就要说说它的最佳良伴——key。 v-for 必须绑定代表唯一值的 key...

Day 0xE UVa10812 Beat the Spread!

Virtual Judge ZeroJudge 题意 输入比赛的分数总和及分差,输出两队分数 需要...

DAY16:Pytorch transforms(上)

torchvision.transforms transforms可以用来改变样本的多样性,例如:旋...