背景简介

全民K歌专辑发布新玩法,传统宣传专辑战绩的流程,从获取数据,到制作海报,到传播,周期长运营成本高,如何快速分享战绩进行荣誉感的传播成为一个亟待解决的问题。

产品:能不能在专辑大事件触发时,自动生成一个大事件长图,供粉丝分享传播?

开发:理论上没问题,尝试下吧…

浏览器端实现方案

开发:大事件长图和专辑详情页大事件tab的视觉效果基本一致,如果能复用可以减少开发时间。

开发:怎么复用呢?

于是便有了下面在浏览器端尝试dom转图片的两种方案:

html2canvas

html2canvas一个在浏览器端通过JS对整个或部分页面进行“截屏”的库。

html2canvas使用方法简单,截屏的核心代码如下:

let imgBase64;html2canvas(htm,{onrendered : function(canvas){//生成base64图片数据imgBase64 = canvas.toDataURL();
});

使用简单,但是坑不少,遇到的坑及解决方案:

1.截图模糊

主要解决思路:

1)将canvas的width和height属性放大为2倍。

2)将canvas的CSS样式width和height设置为原先1倍的大小。

<canvas width="200" height="100" style="width:100px;height:50px;"></canvas>
2.截图不全

源码获取dom高度不准确,修改源码,获取高度后手动传,修改方式如下:

源码:

return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {if (typeof(options.onrendered) === "function") {log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");options.onrendered(canvas);}return canvas;
});

修改后

//添加自定义高度宽度var width = options.width != null ? options.width : node.ownerDocument.defaultView.innerWidth;var height = options.height != null ? options.height : node.ownerDocument.defaultView.innerHeight;return renderDocument(node.ownerDocument, options, width, height, index).then(function (canvas) {if (typeof(options.onrendered) === "function") {log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");options.onrendered(canvas);}return canvas;});
3.截图慢

截图慢得从html2canvas的原理说起,html2canvas并不是真正的截图,而是遍历加载的页面DOM,收集所有元素的信息,然后基于从DOM读取的属性使用canvas来绘制。

基于这个截图原理,慢的问题优化空间不大,而且html2canvas还有些CSS的限制,它只能正确地呈现它支持的CSS属性,完整的CSS属性支持列表,可以在官网查看。

关于慢,最简单的解决方案是在用户操作前提前生成截图。

4.crash

html2canvas截图后,将图片的base64传到客户端的分享组件,当base64超过500k可能导致客户端卡死或crash,如果慢的问题还能忍,那这个问题是真的没法接受的。

svg

除了html2canvas网上也有更轻量更快的库,这些库是基于svg的,尝试了下确实比html2canvas快很多。

svg方案的尝试:

//要转成图片的dom
let htm = '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="auto"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml"><div>这里是页面内容...</div></div></foreignObject></svg>';let DOMURL = window.URL || window.webkitURL || window;let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let img = new Image();
let svg = new Blob([htm], {type: 'image/svg+xml;charset=utf-8'});
let url = DOMURL.createObjectURL(svg);
let imgBase64;img.onload = function () {ctx.drawImage(img, 0, 0);imgBase64 = canvas.toDataURL();
}img.src = url;

svg方案没法绕过的坑:

1.ios下不支持跨域图片

由于安全限制,ios下跨域图片加crossOrigin属性也没法绕过跨域问题。

2.crash

和html2canvas一样,svg转图片后最终也是转base64传分享组件,base64超过500K可能导致的卡死和crash问题也存在。

服务器端实现方案

开发:浏览器端的方案crash问题不能忍,不如在服务器端生成图片,传图片URL到分享组件?

本着最大限度复用代码的初衷,首选了无头浏览器phantomjs截图的方案。

PhantomJS

PhantomJS是基于WebKit内核的无头浏览器,提供浏览器环境的命令行接口,我们可以进行网页截图、抓取网页数据等操作,更多详情可以去PhantomJS官网查看。

安装PhantomJS时,注意安装以下依赖:

sudo yum -y install gcc gcc-c++ make flex bison gperf ruby openssl-devel freetype-devel fontconfig-devel libicu-devel sqlite-devel libpng-devel libjpeg-devel

服务器端方案选择的是phantomjs-node库,实现截图的核心代码如下:

var sitepage = null;
var phInstance = null;
phantom.create().then(instance => {phInstance = instance;return instance.createPage();}).then(page => {let htm = ['<!DOCTYPE html>','<html lang="zh-cn">','<head>','<meta charset="utf-8">','</head>','<body style="background:#fff">','<div>'+ new Date() +'</div>','</body>','</html>'].join("");page.property('content',htm);page.render('./test.png').then((err) => {phInstance.exit()}).catch(err => {phInstance.exit();})}).catch(error => {phInstance.exit();});

PhantomJS遇到的坑也不少,主要是环境问题:

1.没截图生成

开发:在mac上和windows上生成截图正常,部署到测试环境后不能生成截图,打印PhantomJS日志,没有明确的报错信息。linux下权限问题?

查看PhantomJS和目录权限,PhantomJS没有写权限,修复权限问题,图片仍然不能生成。

开发:字母命名的截图正常生成,不支持图片文件名包含数字?

一番验证,截图名包含数字phantomjs-node不能正常生成图片文件。

2.截图空白

开发:颜色和图案均能够渲染到截图中,只有文字不能渲染,字体有问题?

确认测试机中字体目录为空,更新字体,文字终于能正常渲染到截图中。

3.截图模糊

又是模糊问题…

css使用相对rem单位,PhantomJS截图是设置缩放参数:

//css
html{font-size: 100px;}
.owner_avatar{width:.30rem;height: .30rem;border-radius: .30rem;margin-right: .10rem;}
.events_img{width: .50rem;height:.50rem;}//phantomjs缩放处理
page.property('viewportSize',{width:828,height:736});
page.property('zoomFactor',2)
page.property('content',htm);
4.截图加载慢

模糊问题设置2倍图后,图片大小暴涨到6M+,导致加载慢,设置截图质量:

page.render(fileName,{quality:85}).then((err) => {phInstance.exit();
})
5.截图慢

PhantomJS生成一个最简单的截图,耗时2S左右,这个速度显然是不能接受的,暂时没找到比较好的优化方式。

node canvas

node canvas扩展了canvas API以提供与节点的接口,例如流式传输PNG数据,转换为Buffer实例等,更多介绍可以去node canvas官网查看。

node canvas的环境搭建比较麻烦,依赖库与PhantomJS类似,这里就不列举了。

绘制图片的核心代码:

const { createCanvas, loadImage } = require('canvas');
const canvas = createCanvas(200, 200);
const ctx = canvas.getContext('2d');ctx.font = '30px';
ctx.fillText('test', 50, 100);loadImage('test.jpg').then((image) => {ctx.drawImage(image, 0, 0, 70, 70);
})

node canvas与下面imagemagick的方案对比,imagemagick的性能更好,node canvas没再深入只实现了简单demo,踩坑不多。

ImageMagick 与 GraphicsMagick

ImageMagick是一套功能强大、稳定而且免费的工具集和开发包,可以用来读、写和处理超过90种的图片文件,包括流行的TIFF、JPEG、GIF、 PNG、PDF以及PhotoCD等格式。

ImageMagick可以根据web应用程序的需要动态生成图片, 还可以对一个(或一组)图片进行改变大小、旋转、锐化、减色或增加特效等操作,并将操作的结果以相同格式或其它格式保存,对图片的操作,即可以通过命令行进行,也可以用C/C++、Perl、Java、PHP、Python或Ruby编程来完成。更多详情可在ImageMagick官网查看。

GraphicsMagick是从 ImageMagick 5.5.2 分支出来的,据说它变得更稳定和优秀,更多详情可在GraphicsMagick官网查看。

看起来GraphicsMagick是更好的选择,但是由于node gm这个库没有实现GraphicsMagick的半透明和圆角支持,而且针对专辑的大事件长图做了一些性能对比两者差异不大,所以选择使用ImageMagick。

node gm切换ImageMagick的方式非常简单,只要加以下设置:

var gm = require('gm');
var imageMagick = gm.subClass({ imageMagick: true });

不可避免的,使用ImageMagick也遇到一些坑:

1.半透明遮罩

设计:专辑封面背景使用白透明遮罩,遮罩的颜色根据封面图来定,深色封面图用白色文字,浅色封面图用黑色文字。

开发:OK,先canvas获取封面图颜色信息,再判断颜色深浅

//RGB与YUV互转,Y>=128 为浅色
Y'= 0.299*R' + 0.587*G' + 0.114*B'
U'= -0.147*R' - 0.289*G' + 0.436*B' = 0.492*(B'- Y')
V'= 0.615*R' - 0.515*G' - 0.100*B' = 0.877*(R'- Y')
R' = Y' + 1.140*V'
G' = Y' - 0.394*U' - 0.581*V'
B' = Y' + 2.032*U'//ImageMagick设置透明色
.fill("rgba(0,0,0,.5)")
2.头像圆角

设计:这些头像要用圆角哦。

开发:OK(还好ImageMagick支持圆角)

.fill("avatar.jpg")
.drawCircle(80,120,30,120)

ImageMagick圆角图片实现方式与canvas类似,画一个圆,然后用头像图片去填充来实现头像圆角。

3.昵称emoji表情

ImageMagick绘制昵称中的表情图比较麻烦,使用支持emoji的字体,尝试过Twitter的彩色emoji字体,但是ImageMagick有BUG,不能还原为彩色的。

最终解决方案:

1)使用等宽字体,方便计算精确的emoji位置

2)ImageMagick绘制昵称中的表情图片

.draw("image Over " + size + " " + url)

ImageMagick性能优化:

优化前:

优化后:

ImageMagick生成单张图片耗时100ms左右,但是并发请求多了平均耗时就暴涨到3S+,这个速度显然是不能接受的,经过一番优化后将平均耗时降到1S左右,主要优化点如下:

1.gm代码拼接,VM中执行

多次调用gm多次操作图片,严重影响性能,将图片操作代码拼接成字符串,在VM中执行,只调用一次gm,核心代码如下:

let sandbox = {gm : imageMagick,start : Date.now()
}//计算图片高度
let offset = getOffset();
let qrcodeStr = getQrcodeStr();
let titleStr = (function(){return ['.fontSize(24)','.fill("gray")','.drawText(164,152,"我是标题")'];
})();let str = 'gm(828,'+ offset.height +',"#fff").font("'+ FONTS +'",48)'+ titleStr + qrcodeStr +'.quality(90).write("test.jpg",function(err){console.log(err || Date.now() - start)})';let script = new vm.Script(str);
let context = vm.createContext(sandbox);script.runInContext(context);
2.mpc格式

mpc是ImageMagick提供的一种持久高速缓存格式,减少对图像格式进行解码和编码像素的开销。

mpc生成两个文件:

1)一个扩展名.mpc保留了与图像或图像序列相关的所有属性(例如宽度,高度,色彩空间等)。

2)一个扩展名.cache,是本地原始格式的像素缓存。

读取mpc图像文件时,ImageMagick读取图像属性,并将内存映射到磁盘上的像素缓存,无需解码图像像素,不过mpc的文件大小比其他图像格式大。

mpc图像文件适用于一次写入,多次读取模式,使用mpc将图像直接映射到内存,而不是每次重新读取和解压源图像。

3.Q8版本

ImageMagick Q16版本允许在不缩放的情况下读写16位图像,但像素缓存消耗的资源是Q8版本的两倍,Q8版本的执行速度通常比Q16版本要快。

像素缓存消耗 = 宽度*高度*位深度/ 8 *通道Q8位深 = 8 Q16位深 = 16通道 = 红 + 绿 + 蓝 + 阿尔法强度

更详细的性能优化信息可在ImageMagick Architecture查看。

总结

web端实现实时图片生成采坑挺多,目前ImageMagick的方案还有些性能瓶颈,持续优化中。换个思路,如果传递页面URL,由客户端渲染页面,实现截图,或许是更优的方案,目前还没尝试,值得一试…

转载于:https://www.cnblogs.com/liuhao-web/p/8422249.html

web实时长图实践--摘抄相关推荐

  1. 大恒相机开发实践(1)——实时采图

    目录 前言 正文 准备工作 设备的初始化 设备信息的获取 实时采图 将采集到的Buffer传上显示层 总结 前言 本篇博客稍微记录一下我所写的插件.具体内容是有关于大恒相机的,关于这个相机,相信搜索到 ...

  2. MIL开发实践(2)——MIL实时采图

    前言 前言 效果图 正文 初始化 实时采图 总结 前言 网上找到的关于MIL实时采图的代码基本上都是只有很老的代码,但其实MIL是有自带的关于采图方面的QT的demo的.这里给出地址,应该比一些普通的 ...

  3. Basler Blaze-101开发实践(1)——实时采图

    目录 前言 效果图 环境安装 SDK下载 正文 相机的初始化 实时采图 开始抓图 抓图函数 停止抓图 总结 前言 关于Basler的相机还是很常见的,网上也有很多代码可以参考,但这个相机的版本是Bla ...

  4. Web端即时通讯实践干货:如何让WebSocket断网重连更快速?

    本文作者网易智慧企业web前端开发工程师马莹莹.为了提升内容质量,收录时有修订和改动. 1.引言 在一个完善的即时通讯IM应用中,WebSocket是极其关键的一环,它为基于Web的即时通讯应用提供了 ...

  5. 维护几十种语言和站点,爱奇艺国际站WEB端网页优化实践

    1.前言 爱奇艺国际站(www.iq.com)提供了优质的视频给海外各国用户,自上线以来,现已支持几十个国际站点,并且在东南亚多个国家保证了海量用户高速观看体验. 国际站业务的特点是用户在境外访问,后 ...

  6. Web前端开发最佳实践(8):还没有给CSS样式排序?其实你可以更专业一些

    前言 CSS样式排序是指按照一定的规则排列CSS样式属性的定义,排序并不会影响CSS样式的功能和性能,只是让代码看起来更加整洁.CSS代码的逻辑性并不强,一般的开发者写CSS样式也很随意,所以如果不借 ...

  7. 阿里1688实时数据工程实践

    导读:在天猫.淘宝网购过程中,商品的推荐与广告的前端展示是怎样的?两者的底层数据服务又是怎样构建的?今天跟大家讲述面向阿里1688业务的实时数据工程实践. 本次分享主要分为三部分:首先讲解实时数据工程 ...

  8. 给图片下方加水印_别再看不起美图秀秀啦,想要做长图,批量加水印,用它超级方便...

    Hello大家好,我是撒娇的小肉片. 时隔超长时间的更新,不知道还有多少人记得我哈哈.今天想要和大家分享的是如何批量加水印,如何拼成长图,操作简单,你值得拥有哦~ 最重要的是免费!免费!完全免费! 由 ...

  9. Web 实时消息推送详解

    title: Web 实时消息推送详解 category: 系统设计 head: meta name: keywords content: 消息推送,短轮询,长轮询,SSE,Websocket,MQT ...

最新文章

  1. 策略模式——Strategy
  2. Split-plot设计 SAS实践
  3. 广播网关GPC为MDS多媒体调度再添虎翼
  4. Jzoj5421 嘟嘟噜
  5. hibernate级联 cascade属性(转)
  6. 5分钟了解CDN 加速原理
  7. 设计三极管放大电路有哪些技巧?尤其是假设
  8. TS流基本知识【HI3798 AVPLAY播放TS流】
  9. android三国2,三国演义安卓单机版
  10. FlashFXP 在win10下不能拖放操作的解决方法
  11. VideoScribe--超好用的手绘动画制作软件
  12. 电子学会2021年3月青少年软件编程(图形化)等级考试试卷(二级)答案解析
  13. Alcatel-Lucent 命令记录
  14. 利用模型算法部署图像识别_利用这些技巧增强您的图像识别模型
  15. python大数据入门书籍_初学大数据,一定要知道这些
  16. git每次都弹窗提示输入用户名和密码的解决方法
  17. [译]用C#创建一个屏幕保护程序
  18. 巡检路线排班问题matlab,对巡检线路的排班数学模型分析.doc
  19. 初始C语言 C生万物
  20. mysql 远程超级用户,MySQL中,预设的、拥有最高权限超级用户的用户名为( )

热门文章

  1. linux it资产管理系统,开源IT资产管理软件(GIPI)
  2. 电信卡流量套餐超40G后,该如何解除网速限制?
  3. Android分区详解:boot, system, recovery, data, cache 以及 misc
  4. c# 在 word指定位置插入文字和图片(替换 书签)
  5. 排序算法C#实现之快速排序详解
  6. Use Case新手上路
  7. python画spc控制图_实施SPC控制图的八个步骤详解
  8. 城固一中2021高考成绩查询,陕西高中学校排名2021年最新排名 附往年录取分数线...
  9. GPIO口有关上拉电阻和下拉电阻推挽输出开漏(OD)和开集(OC)
  10. 查询Windows下的dll/exe所依赖的文件