视频转GIF小工具,原生javascript + gif.js + canvas,自定义制作表情包

Posted 花子占爷丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了视频转GIF小工具,原生javascript + gif.js + canvas,自定义制作表情包相关的知识,希望对你有一定的参考价值。

一款视频转gif的小工具,可调节开始时间、持续帧、分辨率、动画速度、流畅度、质量,支持在线预览、下载等

很多时候看到视频中的某个片段,觉得特别搞笑,就想做成表情包,可是很多视频转gif的网站都要收费,登录注册之类的,很是脑壳疼,于是乎,我决定用 gif.js 自己制作一个

原理:创建canvas画布获取视频的帧数和预览帧的blob,再利用 gif.js 将每一帧动画的 blob 组合起来就生成了gif 动画。

项目介绍
未选取视频页面展示

转换后页面展示

主逻辑部分

一进页面,开一个定时器,由页面控制选取帧的速度来控制流畅度,创建一个由用户决定分辨率的canvas的画布分离出每一帧后,再进行合并,并且将合并到一起的每一帧展示到页面上

function gifs() 
    loading(flag_loading = true)
    var timer = setInterval(()=> 
        video = document.getElementById("video");	//获取页面中的video元素
        canvas = document.createElement("canvas"); // 创建一个画布
        if (resolut[0] && resolut[1]) 
            canvas.width = resolut[0]
            canvas.height = resolut[1]
         else 
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
        
        canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); // getContext:设置画布环境;drawImage:画画
        var imgurl = canvas.toDataURL("image/png");
        gif.addFrame(canvas,  copy: true, delay: speed )
        imglist.push(imgurl);
        video.play()
        if (imglist.length > video_timer) 
            // 添加显示
            for (let _x = 0; _x < imglist.length; _x++) 
                let _img = document.createElement("img")
                _img.src = imglist[_x]
                _img.classList.add('image')
                document.querySelector('.top_right').appendChild(_img)

            
            clearInterval(timer);
            setTimeout(function () 
                gif.on('finished', function (blob) 
                    console.log(imglist)
                    console.log(blob);
                    blobs = blob
                    video.pause()
                    let gifImg = document.createElement('img')
                    gifImg.src = URL.createObjectURL(blob)
                    gifImgSrc = URL.createObjectURL(blob)
                    document.querySelector('.bottom_left').appendChild(gifImg)
                    loading(flag_loading = false)
                    fileNames = +new Date() + '.gif'
                    _dowonLoad(bytesToSize(blob.size), fileNames, blob)
                );
                gif.render();
            , 200)
        
    , smooth)

选取视频逻辑

选取本地视频并创建播放器

function selectVideo() 
	var file = document.getElementById('file').files[0];
	if (!file) return;
	var url = URL.createObjectURL(file);
	console.log(url);
	let _video = document.createElement("video")
	_video.src = url;
	_video.classList.add('video')
	_video.setAttribute('id', 'video')
	_video.muted = true;
	_video.controls = true;
	// 视频加载完毕时
	_video.addEventListener('loadedmetadata', (e) => 
		console.log(parseInt(_video.duration));
		let _range = document.querySelector('#range')
		_range.max = _video.duration
	)
	document.querySelector('.video_box').appendChild(_video)
	isShowElsemnt(flag = true)

进度条处理逻辑
function ranges() 
	let inner = document.querySelector('.range_inner')
	let _range = document.querySelector('#range')
	let _video = document.querySelector('.video')
	inner.innerhtml = _range.value + 's'
	_video.currentTime = _range.value

动画持续帧逻辑
function timer() 
	let inner = document.querySelector('.timer_inner')
	let _timer = document.querySelector('#timer')
	inner.innerHTML = _timer.value + '帧'
	video_timer = _timer.value

分辨率大小逻辑
function selectOpt() 
	let select = document.getElementById('selectopt');
	let index=  select.selectedIndex; 
	let val = select.options[index].value;
	let str = val.split('x')
	console.log(val);
	if (val == '保持原尺寸') 
		resolut[0] = null
		resolut[1] = null
	
	resolut[0] = str[0]
	resolut[1] = str[1]

动画速度逻辑
function selectspeed()
	let selectspeed = document.getElementById('selectspeed');
	let index=  selectspeed.selectedIndex; 
	let val = selectspeed.options[index].value;
	console.log(val);
	if (val == '正常') 
		speed = 150
	 else if (val == '慢') 
		speed = 400
	
	else if (val == '中') 
		speed = 300
	else if (val == '快') 
		speed = 80
	
	else if (val == '较快') 
		speed = 10
	

画质逻辑
function selectmass() 
	let selectmass = document.getElementById('selectmass');
	let index=  selectmass.selectedIndex; 
	let val = selectmass.options[index].value;
	console.log(val);
	if(val == '高') 
		quality = 30
	else if ('中')
		quality = 20
	else 
		quality = 1
	

流畅度逻辑
function selectsmooth() 
	let selectsmooth = document.getElementById('selectsmooth');
	let index=  selectsmooth.selectedIndex; 
	let val = selectsmooth.options[index].value;
	console.log(val);
	if(val == '正常') 
		smooth = 200
	 else 
		quality = 50
	

字节转换逻辑
function bytesToSize(bytes) 
	if (bytes === 0) 
	    return '0 B';
	
    let k = 1024;
    let sizes = ['B','KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    let i = Math.floor(Math.log(bytes) / Math.log(k));
    let num = (bytes / Math.pow(k, i)).toFixed(1) > Math.floor((bytes / Math.pow(k, i))) ?
        (bytes / Math.pow(k, i)).toFixed(1) : Math.floor((bytes / Math.pow(k, i)));
        console.log(num + ' ' + sizes[i]);
    return num + ' ' + sizes[i];
 
结语

至此,项目就算完成了,后续还会持续完善,有什么不合理的地方欢迎下方留言指点一二,最后奉上项目源码

完整项目地址

视频转gif:前端(小程序)实现截取视频画面图片

最近在自己的小程序实现视频转gif的功能,开发过程中发现网上的很多方案在小程序中实现效果不好或不能实现,比如:
使用前端canvas方案实现的(gif.js,gifshot.js等),在h5上效果没问题,但是在小程序中获取数据的时候极慢;
后来就转而使用uniCloud的云函数方式实现,即使用node.js实现。node.js使用node-canvas后端生成gif,这个是可以的,但是uniCloud使用的服务器node版本自己无法升级,导致想要发布,就只能放弃这个方案。
自己研究了很久,最终使用前端canvas按间隔时间截取一组图片,然后上传到uniCloud,再通过node的gif-encoderget-pixels等第三方包在云服务端将图片合成gif文件,上传到服务器,就可以在小程序将文件下载到本地了。

实现该功能分两步,第一步是将视频按时间间隔截取图片;第二部:将视频按顺序拼接成gif图,并保存导出。本文章先介绍如何在前段将视频画面截图。

云函数node.js 将多图合成gif的代码和思路参考:
视频转gif(二):后端,云函数nodejs实现多图转gif

获取video元素,作为参数传给ctx.drawImage(video,),setInterval每间隔一段时间截图画面。

// 获取canvas
const getCanvasNode = (id, instance) => 
			const query = uni.createSelectorQuery().in(instance);
			const queryCvs = query.select(`#$id`).node();
			
			return new Promise((resolve, reject) => 
				queryCvs.exec(res => 
					resolve(res);
				);
			);
		

// 获取video节点
const getVideoContext = (id, instance) => 
			const query = uni.createSelectorQuery().in(instance);
			const queryVideo = query.select(`#$id`).context();
			return new Promise((resolve, reject) => 
				queryVideo.exec(res => 
					resolve(res);
				);
			);
		
async drawNative() 
			const  width, height, duration  = this.videoInfo; // 实现获取到的视频的信息
			
			// 获取video元素和canvas元素(这里是uniapp 实现,网页版可用getElementById获取)
			const videoRes = await getVideoContext('video-play', this);
			const video = videoRes[0].context;
			const canvasRes = await getCanvasNode('cas-item', this);
			const canvas = canvasRes[0].node; 
			// 改变canvas的尺寸,适配横屏视频和 竖屏视频
			canvas.width = this.cvsWidth.replace('rpx', '')
			canvas.height = this.cvsHeight.replace('rpx', '')
			
			const ctx = canvas.getContext('2d');
			// 截取视频的张数
			this.intTime = duration * 20; // 每个多少毫秒截取 2s-> 40 3s -> 60 5s -> 100
			let computedLen = Math.ceil((duration * 1000) / this.intTime); // 计算可截取的数量
			const imgNameDate = Date.now(); // 用与命名截取的图片
			try 
				
				this.timer = setInterval(async () => 
					// 微信开发工具里canvas不会动,但是真机可以
					if (computedLen < 1) 
						clearInterval(this.timer);
						// 截取完成,将图片上传到存储
						this.uploadToOssTmp(); 
						return;
					
					// 使用canvas context drawImage将视频画面绘制到画布
					ctx.drawImage(video, 0, 0, width, height, 0, 0, canvas.width, canvas.height);
					// 从canvas获取图片像素数据
					const imgData = canvas.toDataURL('image/png', this.qualityVal);
					// genImg将imgData转换为图片base64并保存
					computedLen >= 1 && this.genImg(imgData, imgNameDate, computedLen);
					computedLen--;
				, this.intTime);
			 catch (err) 
			
		,
// 图片像素数据转base6并保存
async genImg(imgData, name, count, loading) 
	const fm = wx.getFileSystemManager();
	let filePath = `$this.baseTmpDirName/$name_$count.png`;
	this.imgBase64.push(imgData.split(',')[1]);
,

到此,视频转GIF的第一步就完成了,接下来就是要实现多张图片转为GIF的功能。

也欢迎,扫码体验一下,如有使用问题可以联系我

以上是关于视频转GIF小工具,原生javascript + gif.js + canvas,自定义制作表情包的主要内容,如果未能解决你的问题,请参考以下文章

视频转gif:前端(小程序)实现截取视频画面图片

怎么把视频里的一小段视频做成动态图片?

怎么样将视频转换成gif?

怎么把视频做成动图

为啥将视频转为GIF后,文件反而比视频大的多了?

LICEcap:GIF屏幕录制工具