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. pywin32模块安装

    安装流程: 1.查看python版本和位数: 2.下载对应的的pywin32,下载目录任意 https://sourceforge.net/projects/pywin32/files%2Fpywin ...

  2. docker pull 镜像报错

    [root@localhost ~]# docker pull ningx Using default tag: latest Trying to pull repository docker.io/ ...

  3. C#线程--5.0之前时代(一)--- 原理和基本使用

    一.开篇概念明晰: 多任务: 协作式多任务:cpu可以处理多种任务,但是这些任务是排队等候的,当cpu在处理一个任务的时候,其他的任务被锁定,只有当cpu处理完当前任务,才可以继续处理下一个任务(专一 ...

  4. LeetCode 15 3Sum [sort] <c++>

    LeetCode 15 3Sum [sort] <c++> 给出一个一维数组,找出其中所有和为零的三元组(元素集相同的视作同一个三元组)的集合. C++ 先自己写了一发,虽然过了,但跑了3 ...

  5. js将一篇文章中多个连续的<br>标签替换成两个连续的<br>标签

    写本文的目的是今天恰好有一个之前做SEO的同事问我怎样把一篇文章中多个连续的br标签替换成两个连续的br标签,这里就牵涉到SEO层面的问题了. 在做SEO优化的时候,其中有一个需要注意的地方就是尽量减 ...

  6. 837B. Balanced Substring

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  7. Linux神奇命令之---tar

    在生产中会常常用到压缩,解压缩,打包,解包等,这时候tar这个万能的小老弟就是是必不可少的了.linux中最流行的tar是麻雀虽小,五脏俱全,功能强大. tar命令可以为linux的文件和目录创建档案 ...

  8. Java面试题中常考的容易混淆的知识点区别

    以下是我收集的Java编程里各种区别,供Java学习爱好者参考,这些区别都是每次Java面试中常考的,大家好好掌握,如有失误请留言指出.想要获取Java详细全套学习资料请到上海尚学堂官网获取. 1.H ...

  9. [Swift]LeetCode88. 合并两个有序数组 | Merge Sorted Array

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note: T ...

  10. [Swift]LeetCode254.因子组合 $ Factor Combinations

    Numbers can be regarded as product of its factors. For example, 8 = 2 x 2 x 2; = 2 x 4. Write a func ...