WEB 版 GIF 编辑器

之 从无到有&从外到内       

https://gif.pyzy.net

 

huzunjie @ 75team

2015-12

需求背景

技术调研

  1. GIF素材收集
  2. 编译处理+技术选型
  3. GIF文件存储

技术调研

资源收集:
书签栏注入JS工具截获截图,作为帧素材备用。

- 1

(function(){
    if(!window.$ || !$.getUrlParam) return alert('环境不对头,不能使用。');
    
    var qihoo_playerEl = $("#qihoo_player")[0];
    if(!qihoo_playerEl) return alert('已经废弃了~');
    
    var ipcData = (window.playInfo && window.playInfo.origin) || { sn:$.getUrlParam('sn') };
	var sn = ipcData.sn;
    if(!sn) return alert('没有取到SN号,无法继续。');
    
    
    
    //设定关键帧采集间隔时长数
    var delayTime = prompt('请设定关键帧采集间隔时长数(秒,10-N)',30);
    if(isNaN(delayTime) || delayTime<1){
        return alert('无效帧间隔时长'+(delayTime<1?'(这是生成定格动画的,不是录像,所以时间不能太短)':'')+',还想玩从新来过吧!');   
    }
    delayTime *= 1000;
    
    //设定帧数
    var frameCount = prompt('请设定要采集的关键帧总数(10-50)',10);
    if(isNaN(frameCount) || frameCount<1){
    	return alert('无效帧总数,还想玩从新来过吧!');   
    }
    ///if(frameCount>100)return alert('你要疯啊? 帧数太多会搞死的...');
    

    var pr = '';
    //监控被移动到后台则提示
    document.addEventListener("visibilitychange", function(e){
        if(document.visibilityState=='hidden'){
            document.title='[页面已切换到后台,可能无法正常截图]';
        }else{
            document.title=pr;
        }
    });  
    
    var _fCount = 0, _date = new Date(), tit = ipcData.titlePub || $(".title").text()||sn;
    
    /* 截屏上传图片 */
    function startUpload(){
        
        qihoo_playerEl.uploadScreenshotImg({
            url: 'http://'+(window._s_h_1?'1.':'')+'huzj.sinaapp.com/gif/upload.php',
            from: tit,
            sn: sn
        });
        
        _fCount ++;
        pr = document.title='截图进度:'+(Math.round(_fCount/frameCount*100))+'% ('+(_fCount+'/'+frameCount)+')';
        
        /* 输出进度日志 */
        console.log("计划获取总帧数:"+frameCount, '当前截取第'+_fCount+'帧', ' 已耗时(秒):'+ Math.round( (new Date() - _date)/1000 ),' 效果预览:','http://huzj.sinaapp.com/gif/index.html?sn='+sn+'&step=1&acount='+_fCount );

        if(_fCount<frameCount){
            setTimeout(startUpload, delayTime);
        }else{
            alert('已完成摄像机['+tit+']的截图');
        }
    }
    startUpload();
    
})();

技术调研

加工处理:
基于一个用 CoffeeScript 编写的gif处理库 gif.js
合成为BLOB形式的GIF文件。

- 2

/*** gif.js使用方法 ***/

// 实例化一个GIF对象
var gif = new GIF({
  workers: 2,
  quality: 10
});

// 通过img元素对象来创建帧
gif.addFrame(imageElement);

// 通过canvas元素对象创建帧 示例:http://jnordberg.github.io/gif.js/tests/canvas.html
gif.addFrame(canvasElement, {delay: 200});

// 通过canvas上下文对象创建帧
gif.addFrame(ctx, {copy: true});

// 通过video当前画面创建帧 示例:http://jnordberg.github.io/gif.js/tests/video.html
gif.addFrame(videoElement, {copy: true});

// GIF生成完毕后通过异步事件监听做相应处理
gif.on('finished', function(blob) {
  window.open(URL.createObjectURL(blob));
});

// 渲染GIF图
gif.render();

技术调研

文件保存:
URL.createObjectURL( BLOB ) 生成GIF URL,输出到页面;
并使用A标签的download属性,自定义GIF文件名称。
实现工具雏形

- 3

/*** gif 存储 ***/

// GIF生成完毕后通过异步事件监听做相应处理
gif.on('finished', function(blob) {
    var blobUrl = URL.createObjectURL(blob);
    retBoxEl.innerHTML = '<a href="'+blobUrl+'" download="水滴直播['+sn+']动态图.gif">'+
                             '<img src="'+blobUrl+'">'+
                         '</a>';
});

gif.js内部逻辑流程

图片资源加载成功

drawImage 绘制
​图片到canvas上

 getImageData 从canvas取得帧图像的像素矩阵数据

new Worker
启动子进程 

addFrame

逐帧将imageData 
post给子进程 render

子进程完成24位色到
8位色的计算转换

ArrayBuffer
回传给主进程

使用Blob API 创建
image/gif 图片文件

postMessage

postMessage

服务端数据存取

  • SaeStorage 进行图片和JSON数据存取
  • 通过 rewrite 规则实现伪静态图片URL

确定需求&产品设计

  1. UI样式
    • 色调
    • 布局
    • 细节
  2. 交互功能
    • 核心功能
    • 附加功能
    • 锦上添花

产品设计 - UI

  1. 色调 - “继承、发展”现成UI效果
  2. 布局 - boxFlex 基于可操控性考虑
    1. 收缩比率 flex-shrink  默认值不统一,
      不参与弹性计算的子容器需reset为0
    2. 小点心 width: calc(100% - 100px );
    3. 小点心 CSS3 counter
  3. 重置默认组件样式,
    pointer-events及各种伪类的使用。

图形化界面降低使用成本 UI 雏形

产品设计 - 核心功能

  1. 导入素材 - 按水滴设备、从本地
  2. 逐帧或批量编辑
    元件、尺寸、坐标、样式、序列的编辑
  3. 导出便于传播的GIF图

产品设计 - 附加功能

  1. 存档
  2. 存档的导入导出
  3. 本地文件导入
  4. 新建
  5. 添加图片元件
  6. 添加手绘元件

产品设计 - 锦上添花的功能

  1. 侧边栏展开收起
  2. 网格参考线
  3. 标尺工具
  4. 放大缩小
  5. 宽高比例锁
  6. 拖拽控制编辑器区域大小
  7. 帧元素的事件编程
    -- 逻辑控制公共元素显隐
  8. 帧延迟时长的动态编程
  9. 接入多说...

技术实施

  1. 基于QClient设计思路
  2. 围绕数据模型的事件消息机制
    gifEditor.data._base_data
  3. 使用 $.tmpl 缓存+动态渲染
  4. MV* ?双向绑定? 

UI和具体需求有了,接下来考虑功能的实现

先看下代码,再继续往下看PPT总结

数据交互流程

  • UI:提供可视化数据操作界面+渲染预览效果
  • Logic:业务层面复杂场景下数据API的整合处理
  • Data:数据模型及相应读写操作API

功能划分&技术选型

  • jQuery
  • gif.js             图片编译
  • gif_editor.js 业务逻辑
  • common.js  公用方法
  • tmpl.js         模板解析
  • h5_upload   H5上传组件
  • rulers.js        标尺组件

待探索的方向们

  1. 泛摄像头’||视频转GIF - 播放器或服务端方向
  2. 优化或换掉底层GIF.JS - 开销、效率
  3. JS浮点计算溢出导致不准确 - 服务端?
  4. 关注其他动图格式进展 - 性能、质量
  5. 趣味性挖掘 - 定格1定格们
  6. GIF资源站还蛮热门的 - 工具、资源、社交

回顾

  • 探寻需求背景
  • 可行性技术调研
  • UE与产品设计
  • 技术设计与实施
  • 未来方向

Q & A

Made with Slides.com