准备工作
- 1个gif图
- 1个短video,最好只有几秒
- 下载gif.js源码,用于生成gif图
- 安装local-web-server,因为直接访问webWorker(gif.js有使用到)会出现跨域问题,安装后任何文件的访问都可以使用http协议了
- 下载FileSaver.js,可将生成的gif图保存到本地
使用canvas截gif图
最初始的想法是想用gif图来生成带文字的gif图,但是发现使用canvas绘图时总是截gif的第一帧图,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#result {
background: #eee;
}
.wrap {
display: inline-block;
vertical-align: top;
text-align: center;
}
canvas {
background: #eee;
}
</style>
</head>
<body>
<img src="./gifs/throw_computer.gif" alt="" id="imgEle">
<div class="wrap">
<canvas id="canvas"></canvas>
<div>截图预览</div>
<button id="btn">截图</button>
</div>
</body>
<script>
function main() {
let btn = document.getElementById('btn')
// 画布元素
let canvas = document.getElementById('canvas')
let context = canvas.getContext('2d')
// 绘制图片
let img = document.getElementById('imgEle')
img.onload = function () {
btn.addEventListener('click', function () {
context.clearRect(0, 0, canvas.width, canvas.height)
context.drawImage(img, 0, 0, img.width, img.height)
})
}
}
main()
</script>
</html>
效果:
点击这个截图button,总是截取gif的第一帧图,故放弃使用gif来生成有文字的gif的想法。
使用canvas截取视频图片再生成gif
生成gif需要使用到 gif.js 以及 gif.worker.js ,效果如下:
说明:最左边的是原视频,中间是在播放视频时用canvas实时绘制图片而生成的动态预览,最右边是在视频播放完后生成跟视频内容一样的的gif图。
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#result {
background: #eee;
}
.wrap {
display: inline-block;
vertical-align: top;
text-align: center;
}
.video-wrap{
position: relative;
}
canvas {
background: #eee;
}
#text {
font-size: 20px;
color: #fff;
position: absolute;
top: 20px;
left: 20px;
}
</style>
</head>
<body>
<div class="wrap video-wrap">
<video id="videoEle" controls width="448" height="250">
<source src="./videos/Saturday_night.mp4" />
</video>
<div id="text">test</div>
</div>
<div class="wrap">
<canvas id="canvas"></canvas>
<div>截图预览</div>
</div>
<div class="wrap">
<img id="result" src="./gifs/loading.gif"></img>
<div>gif预览</div>
</div>
<div>
<button id="btn">保存GIF</button>
</div>
</body>
<!-- gif.js与与gif.worker.js放置在同一文件夹下 -->
<script src="./gifs.js/gif.js"></script>
<script src="./js/FileSaver.js"></script>
<script src="./index.js"></script>
</html>
// index.js
let canvas = document.getElementById('canvas')
let context = canvas.getContext('2d')
// 画布元素
let gifCanvas = document.createElement('canvas')
let gifCxt = gifCanvas.getContext('2d')
let video = document.getElementById('videoEle')
let result = document.getElementById('result')
let gif = null
function main() {
listenVideo()
}
function listenVideo() {
let timeout = null
let width, height
video.addEventListener('canplay', function () {
// 浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不需要停止缓存更多的内容
width = video.width
height = video.height
result.width = canvas.width = gifCanvas.width = width
result.height = canvas.height = gifCanvas.height = height
})
video.addEventListener('play', function () {
// 点击播放按钮
initGif(width, height)
timeout = setInterval(drawImg, 60)
})
video.addEventListener('pause', function () {
// 点击暂停
clear(timeout)
})
video.addEventListener('ended', function () {
// console.log(gif)
// 播放完成
gif.render()
clear(timeout)
})
}
function drawImg() {
context.clearRect(0, 0, canvas.width, canvas.height)
gifCxt.clearRect(0, 0, gifCanvas.width, gifCanvas.height)
context.drawImage(video, 0, 0, video.width, video.height)
gifCxt.drawImage(video, 0, 0, video.width, video.height)
addGifFrame()
}
function addGifFrame() {
// 这里写在setInterval里已经延时过了,所以delay设置为0.01(若设置为0,则会被重置为初始值500)
gif.addFrame(gifCxt, { copy: true, delay: 0.01 })
}
function clear(timeout) {
timeout && clearInterval(timeout)
}
function initGif(width, height) {
gif = new GIF({
width: width,
height: height,
workerScript: './gifs.js/gif.worker.js', // gif.worker.js放置的位置
worker: 4,
})
// 设置文字属性,下一节需要用到
gifCxt.font = context.font = '22px serif' // 设置大小
gifCxt.fillStyle = context.fillStyle = '#fff' // 设置颜色
gif.on('finished', function (blob) {
let url = URL.createObjectURL(blob)
result.setAttribute('src', url)
// TODO:压缩gif图,现在的太大(好像需要放到服务器端压缩,前端不太好压缩)
document.getElementById('btn').addEventListener('click', function () {
// 保存为 hello world.gif,saveAs方法是FileSaver.js里面的方法
saveAs(url, 'hello world.gif')
})
})
}
main()
给生成的gif添上动态文字
为演示方便,这里只是简单的添加视频秒数。 index.js 添加的代码如下:
// index.js
// 其他代码都一样,修改 drawImg 方法
function drawImg() {
context.clearRect(0, 0, canvas.width, canvas.height)
gifCxt.clearRect(0, 0, gifCanvas.width, gifCanvas.height)
context.drawImage(video, 0, 0, video.width, video.height)
gifCxt.drawImage(video, 0, 0, video.width, video.height)
setText() // 添加文字
addGifFrame()
}
// 添加文字
function setText() {
let videoCurrentTime = video.currentTime
let time = parseInt(videoCurrentTime)
let str = '这是第' + time + 's'
context.fillText(str, 150, 80)
gifCxt.fillText(str, 150, 80)
}
这是生成的gif图:
待改进
现在生成的gif图挺大的,并且视频来生成gif图还是感觉不太方便,用gif图然后配上文字直接生成gif图感觉更好点,可以参考这个:libgif-js。另外,现在生成的gif图感觉不是很流畅,不如中间那个用canvas实时获取视频截图流畅,暂时不知道什么原因。