Canvas

Canvas 是 HTML5 新增的组件,就像一个画板,用 js 这杆笔,在上面乱涂乱画

创建一个 canvas

<canvas id="stockGraph" width="150" height="150"></canvas>

let canvas = document.createElement("canvas");

渲染上下文

CanvasRenderingContext2D

使用 canvas.getContext('2d')方法让我们拿到一个 CanvasRenderingContext2D 对象,然后在这个上面画

//用getContext()判断是否支持canvas
if(canvas.getContext){
let context = canvas.getContext('2d');
}

canvas 绘制文字

  • fillStyle = color 设置图形的填充颜色。
  • fillText(text,x,y,[, maxWidth]) 在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的.
  • strokeText(text, x, y [, maxWidth]) 在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的.

设置 font,同 css 的 fong 属性

context.font = "italic 1.2em "Fira Sans", serif";

css 的 font 属性是设置 font-style, font-variant, font-weight, font-size, line-height 和 font-family 属性的简写,或使用特定的关键字设置元素的字体为某个系统字体。

canvas 绘制图片

context.drawImage(img,x,y);
context.drawImage(img,x,y,width,height); //其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标,width 和 height,这两个参数用来控制 当向 canvas 画入时应该缩放的大小
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
名称 作用
img 用来被绘制的图像、画布或视频
sx 可选。img 被绘制区域的起始左上 x 坐标
sy 可选。img 被绘制区域的起始左上 y 坐标
swidth 可选。img 被绘制区域的宽度(如果没有后面的 width 或 height 参数,则可以伸展或缩小图像)
sheight 可选。img 被绘制区域的高度(如果没有后面的 width 或 height 参数,则可以伸展或缩小图像)
x 画布上放置 img 的起始 x 坐标
y 画布上放置 img 的起始 y 坐标
width 可选。画布上放置 img 提供的宽度(可能会有图片剪裁效果)
height 可选。画布上放置 img 提供的高度(可能会有图片剪裁效果)

使用 drawImage 进行切片 drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight),这个在图片合成的时候是利器,该实现中没用过(可以不用美工切好图再用,而是前端直接自己切图)

canvas.toDataURL()

  • 如果该 canvas 的宽度或长度是 0,则会返回字符串"data:,".
  • 如果指定的 type 参数不是 image/png,但返回的字符串是以 data:image/png 开头的,则所请求的图片类型不支持.
  • Chrome 支持 image/webp 类型.
  • 如果 type 参数的值为 image/jpeg 或 image/webp,则第二个参数的值如果在 0.0 和 1.0 之间的话,会被看作是图片质量参数,如果第二个参数的值不在 0.0 和 1.0 之间,则会使用默认的图片质量.

一个在线的精简版 ps 直接网页打开就可以用了http://www.uupoop.com/

img 标签的 onError,onLoad,onAbort

onError:当图片加载出现错误,会触发 经常在这里事件里头写入 将图片导向默认报错图片,以免页面上出现红色的叉叉

onLoad:事件是当图片加载完成之后触发

onAbort:图片加载的时候,用户通过点击停止加载(浏览器上的红色叉叉)时出发,通常在这里触发一个提示:“图片正在加载”

先准备一张图

然后用 ps 切成 4 张图,分别的是东邪西毒南帝北丐

合成 canvas 为图片的函数

compose.html

...
<div class="img-list">
<div><img class="compose" src="./img/timg-(1)_01.png" alt="" /></div>
<div><img class="compose" src="./img/timg-(1)_02.png" alt="" /></div>
<div><img class="compose" src="./img/timg-(1)_03.png" alt="" /></div>
<div><img class="compose" src="./img/timg-(1)_04.png" alt="" /></div>
</div>
... <script type="text/javascript">
window.onload = function() {
var compose = document.querySelectorAll(".compose");
var src_arr = [];
compose.forEach(node => {
src_arr.push(node.src);
}); ComposeCanvas.compose(
[400, 400],
[
{
src: src_arr[0],
type: "image",
mode: "waiting",
pos: [200, 0, 200, 200]
},
{
src: src_arr[1],
type: "image",
mode: "waiting",
pos: [200, 200, 200, 200]
},
{
src: src_arr[2],
type: "image",
mode: "waiting",
pos: [0, 0, 200, 200]
},
{
src: src_arr[3],
type: "image",
mode: "waiting",
pos: [0, 200, 200, 200]
},
{
text: "我是南帝",
type: "text",
mode: "waiting",
pos: [50, 100],
style: {
color: "#333",
font: "30px serif"
}
},
{
text: "我是北丐",
type: "text",
mode: "waiting",
pos: [0, 320],
style: {
color: "#333",
font: "40px serif"
}
},
{
text: "中神通呢?",
type: "text",
mode: "waiting",
pos: [120, 220],
style: {
color: "#333",
font: "40px serif"
}
}
],
function(img) {
console.log(img);
document.getElementById("hello").appendChild(img);
}
);
};
</script>

compose.js

(function(win) {
var ComposeCanvas,
layer = [],
callback,
imgList = [],
canvasWH = [200, 200];
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d"); function backElement(back) {
var $return;
switch (back) {
case "canvas":
$return = canvas;
break;
case "image":
$return = convertCanvasToImage(canvas);
break;
default:
$return = canvas;
}
return $return;
} /**
* 用来处理宽高和图层,返回合成结果
* @param {*} WH canvas的宽高
* @param {*} options canvas的图层
*/
function compose(WH, options, backEvent) {
callback = backEvent;
_handleWH(WH);
_handleLayer(options);
} function convertCanvasToImage(c) {
c = c || canvas;
var image = new Image();
var src = c.toDataURL("image/png");
image.setAttribute("src", src);
image.setAttribute("crossOrigin", "anonymous");
return image;
} function _handleWH(WH) {
canvasWH = WH || [200, 200];
canvas.width = canvasWH[0];
canvas.height = canvasWH[1];
//https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D
context.fillStyle = "transparent"; //画布填充颜色;
context.fillRect(0, 0, canvasWH[0], canvasWH[1]);
} function _handleLayer(options) {
if (Object.prototype.toString.call(options) === "[object Array]") {
layer = options;
} var length = layer.length; for (var i = 0; i < length; i++) {
if (layer[i]["type"] === "text") {
layer[i]["mode"] = "complete";
_checkLayerMode();
} if (layer[i]["type"] === "image") {
var pos = layer[i].pos;
var src = layer[i].src;
imgList[i] = new Image(pos[2], pos[3]);
imgList[i].setAttribute("src", src); imgList[i].onload = (function() {
_loadedImg(i);
})(); // imgList[i].onerror = (function() {
// _errorImg(i);
// })();
}
}
} function _loadedImg(index) {
//https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage
layer[index]["mode"] = "complete";
layer[index]["element"] = imgList[index];
_checkLayermode();
} // function _errorImg(i) {
// console.log("图片加载出错");
// layer[i]["mode"] = "error";
// _checkLayermode();
// } function _checkLayerMode() {
var check = false;
var current = 0;
layer.forEach(function(item) {
if (item.mode === "complete") {
current++;
}
if (current === layer.length) {
check = true;
}
}); check && _draw();
} function _draw() {
layer.forEach(function(item) {
if (item["type"] === "text") {
context.fillStyle = item.style.color;
context.font = item.style.font;
context.fillText(item.text, item.pos[0], item.pos[1]);
}
if (item["type"] === "image" && item["element"]) {
context.drawImage(item["element"], item.pos[0], item.pos[1], item.pos[2], item.pos[3]);
}
}); var img = backElement("image");
return callback && callback(img);
} ComposeCanvas = {
compose: compose,
convert: convertCanvasToImage
}; if (typeof module !== `undefined` && typeof exports === `object`) {
module.exports = ComposeCanvas; //commonjs
} else if (typeof win.define === "function" && (win.define.amd || win.define.cmd)) {
win.define("ComposeCanvas", [], function() {
return ComposeCanvas;
});
} else {
win.ComposeCanvas = ComposeCanvas;
}
})(window);

效果,中神通呢

演示地址

一个合成图片文字要求

需要做到

  1. 能够将多张图片进行合成
  2. 中间能够插入文字
  3. 按照顺序进行画图,上层图层会盖住下层

问题

  1. 图片加载完成事件是个异步的,文字同步画在画板上
  2. onload 和 onerror 事件
  3. 本地解决图片加载问题

我是在 vue 项目里面用(为了能够在没有任何编译环境下用,我没用 es6,如果是在 es6 中写这种 compose 可以用 class, promise , async 配合使用,要舒服很多)

amd,cmd,common 和 js 原生目前实现的模块不一样,是不兼容的,能够直接用 import 引入是 webpack 环境的功劳

可以直接复制粘贴上面的代码建个文件测试,也可以 npm install canvas-compose-image --save

实现的效果

合成了一张图片,在美化下字体就 ok 了,可以直接下载在手机相册中。

项目图还是去掉了,怕收到律师函

错误信息 Failed to execute 'drawImage'

Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'

你在使用 drawImage 方法的时候使用了不正确的元素,不如图片没有加载完或者没获取到正确的,是个 undefined,就会报错。可以试试将第一个参数设为 window,就会直接报错。

需要的节点在错误信息已经明确说了

错误信息 Failed to execute 'toDataURL' on 'HTMLCanvasElement'

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

依然是老朋友,跨域的问题,别双击 html 打开就行了,撘个静态文件服务器就好了。demo 里面有个 server.js,用 node 启动一下就好了。

错误信息 onload 和 onerror 都执行

没解决,只要有 onerror 一定会执行 onerror,注释掉就执行 onload,没明白

npm 发布插件步骤

  1. npm init
  2. 添加过滤文件
  3. npm publish

黑名单模式:.npmignore 文件,没有.npmignore 情况下使用.gitignore 文件。(.gitignore 优先级会高些,小心)

跟.gitignore 一样

白名单模式:package.json 里边配置 files 字段

"files": [
"LICENSE",
"History.md",
"Readme.md",
"index.js",
"lib/"
]

白名单模式:pkg.files 配置 files 字段,只发布配置的文件或目录

{
"files": [
"index.js",
"lib"
]
}

Docs

MDN - canvas

使用 canvas 在前端实现图片水印合成

CSS3 混合模式 mix-blend-mode/background-blend-mode 简介

npm publish

canvas.toDataURL() SecurityError

浏览器同源政策及其规避方法

canvas 问题浅析

使用canvas在前端实现图片水印合成

JS判断单、多张图片加载完成

[JavaScript] canvas 合成图片和文字的更多相关文章

  1. 通过canvas合成图片

    通过canvas合成图片 效果图 页面布局部分 两个图片以及一个canvas画布 <img src="https://qnlite.gtimg.com/qqnewslite/20190 ...

  2. PHP生成小程序二维码合成图片生成文字

    这部分代码是写在项目上的代码,THINKPHP3.1如果迁移到其他的地方应该要稍稍改动一下以适合自己的项目 function get_bbox($text,$fsize,$ffile){ return ...

  3. canvas合成图片 圣诞节新技能戴帽

    <!doctype html><html><head><meta charset="utf-8"><title>Html ...

  4. 用canvas合成图片

    朋友圈有些分享功能是通过长按图片另存来实现的,就像淘宝内部要分享朋友圈的时候一样,这些图片可以用canvas来合成. 获取了img的dom对象以后,进行base64的转. //加载对象$page.ge ...

  5. H5项目开发分享——用Canvas合成文字

    以前曾用Canvas合成.裁剪.图片等<用H5中的Canvas等技术制作海报>.这次用Canvas来画文字. 下图中"老王考到驾照后"这几个字是画在Canvas上的,与 ...

  6. 转《JavaScript中的图片处理与合成》

    引言: 本系列现在构思成以下4个部分: 基础类型图片处理技术之缩放.裁剪与旋转(传送门): 基础类型图片处理技术之图片合成(传送门): 基础类型图片处理技术之文字合成(传送门): 算法类型图片处理技术 ...

  7. Html5 Canvas 实现图片合成

    多个图片合成一张 <!doctype html> <html> <head> <meta charset="utf-8"> < ...

  8. PHP合成图片、生成文字、居中对齐、画线、矩形、三角形、多边形、图片抗锯齿、不失真 高性能源码示例

    function generateImg($source, $text1, $text2, $text3, $font = './msyhbd.ttf') { $date = '' . date ( ...

  9. php 图片添加文字水印 以及 图片合成(微信快码传播)

    1.图片添加文字水印: $bigImgPath = 'backgroud.png'; $img = imagecreatefromstring(file_get_contents($bigImgPat ...

随机推荐

  1. 解决Android Studio 将String类型保存为.txt文件,按下button跳转到文件管理器(解决了保存txt文件到文件管理后,手机打开是乱码的问题)

    不知道为什么保存文件后之前打开一直都OK,就突然打开看到变成乱码了,最后解决了 关键:outStream.write(finalContent.getBytes("gbk")); ...

  2. CFUpdate高速模式下出现Error #2038提示的解决方案

    使用CFUpdate上传文件,在IE模式下是正常的,切换到高速模式下出现提示Error #2038错误,文件无法上传. 向作者了解到需要设置challs_flash_update函数中的a.url为绝 ...

  3. js-day03-事件响应和练习题

    DOM事件编程 事件驱动编程:所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么操作(即调用什么函数).当然事件不仅限于用户的操作. 当对象处于某种状态时,可以发出一个消息通知,然 ...

  4. AI-2.梯度下降算法

    上节定义了神经网络中几个重要的常见的函数,最后提到的损失函数的目的就是求得一组合适的w.b 先看下损失函数的曲线图,如下 即目的就是求得最低点对应的一组w.b,而本节要讲的梯度下降算法就是会一步一步地 ...

  5. NodeJS NPM 镜像使用方法

    每次npm的时候,走国外的镜像,非常的慢,可以配置一下 通过改变默认npm镜像代理服务,以下三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候不用重新配置. 通过config命令 ...

  6. Socket看法

    Socket通常也称做”套接字“,用于描述IP地址和端口,废话不多说,它就是网络通信过程中端点的抽象表示. Socket又称"套接字",应用程序通常通过"套接字" ...

  7. linux系统中的时间

    1.编程显示系统时间: #include <stdio.h> #include <time.h> /* gcc -o fix fixedFormatTime.c ./fix * ...

  8. [python] 溜了,溜了,七牛云图片资源批量下载 && 自建图床服务器

    故事背景: 七牛云最近一波测试域名操作真是把我坑死了!这简直和百度赠送你2T网盘,之后再限速一样骚操作.于是,痛定思痛自己买个云主机.自己搭图床应用! 1.七牛图片批量下载到本地 1.1 曲折尝试 当 ...

  9. Spring使用支付宝扫码支付

    前一段一直在研究支付宝的扫码支付,不得不说,支付宝的文档写的真是一个烂(起码在下刚开始看的时候是mengbi的).文档上面的示例和demo里面的示例长的完全不一样.往往文档上面的例子很简单,而demo ...

  10. zuul网关入门(一、网关具有的功能)

    1. zuul网关入门(一.网关具有的功能) 1.1. 基本场景 1.1.1. API网关的由来 1.1.2. API网关基本功能 1.2. 高级应用 1.2.1. 亮点 可动态发布的过滤器机制 1. ...