写点什么

canvas 从零到一,实际案例

用户头像
关注
发布于: 2021 年 05 月 14 日
1.绘制路径
1.开始绘制:ctx.beginPath(); 重点,隔离产生一个域
2.描边宽度:ctx.lineWidth
3.描边颜色:ctx.strokeStyle = 'red';
4.路径开始点:ctx.moveTo(x, y);
5.路劲目标点:ctx.lineTo(x, y);
6.绘制路径:ctx.stroke();
7.关闭路径:ctx.closePath();
复制代码
2.绘制矩形
1.矩形方法:ctx.rect(x, y, w, h);
2.设置填充颜色:ctx.fillStyle = '#987';
3.执行填充动作:ctx.fill();
4.绘制一个填充的矩形:ctx.fillRect(x, y, w, h); 没有描边
5.绘制一个描边矩形:ctx.strokeRect(x, y, width, height);
6.清除画布:ctx.clearRect(x, y, width, height);
复制代码
3.绘制圆
    ctx.beginPath();    ctx.arc(x, y, r, startAngle, endAngle(Math.PI),);
ctx.moveTo(100, 50); ctx.lineTo(100, 100); ctx.lineTo(150, 100); ctx.fill();
复制代码
4.绘制文字
1.设置字体大小与字体库: ctx.font = `${fontSize}px serif`;
2.设置基准线:ctx.textBaseline = 'middle';
3.绘制字体:ctx.fillText('sdddddddddd但是v', 0, 0);
4.获取文本信息: ctx.measureText(text);
5.demo: ctx.save(); ctx.translate(0, (fontSize / 2)); // 适配安卓 ios 下的文字居中问题 ctx.fillText(text, 0, 0); ctx.restore();
复制代码
5.绘制图片
// 缩放图片,在canvas上展示const maxW = 100;const multiple = img.width /  maxW;ctx.drawImage(img, x, y, img.width, img.height, 0, 0, maxW, img.height / multiple);
复制代码
6.制作渐变线条,矩形
const gradient = ctx.createLinearGradient(20,0, 220,0);
// Add three color stopsgradient.addColorStop(0, 'green');gradient.addColorStop(.5, 'cyan');gradient.addColorStop(1, 'red');
// Set the fill style and draw a rectanglectx.fillStyle = gradient;
复制代码
7.重点方法
1.移动画板的位置:ctx.translate(x, y);
2.旋转画板:ctx.rotate(Math.PI / 180 * Angle);
3.放大缩小画板:ctx.scale(x, y);
4.保存状态:ctx.save();
5.恢复状态:ctx.restore();
6.ctx.transform(a, b, c, d, e, f); a:水平缩放 b:垂直倾斜 c:水平偏斜 d:垂直缩放 e:水平平移 f:垂直平移 7.裁剪路径的区域:ctx.clip(path,"nonzero":非零缠绕规则,默认规则。"evenodd");
复制代码
8.封装生成圆形图片的方法
/** * @description: 生成圆角矩形 * @param {*} ctx * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @param {number} r * @param {string} color * @param {string} type:fill | stroke | undefined * @return void */const roundedRectangle = (ctx, x: number, y: number, w: number, h: number, r: number, color = '#000', type?: string | undefined) => {    const PI = Math.PI / 180;    ctx.translate(x, y);    ctx.save();    ctx.beginPath();    // ctx.moveTo(0, 0);    // ctx.lineTo(w, 0);
ctx.arc(w - r, r, r, PI * 270, PI * 360); // ctx.lineTo(w, h);
ctx.arc(w - r, h - r, r, PI * 0, PI * 90); // ctx.lineTo(0, h);
ctx.arc(r, h - r, r, PI * 90, PI * 180); // ctx.lineTo(0, 0);
ctx.arc(r, r, r, PI * 180, PI * 270); ctx.closePath(); ctx.fillStyle = color; switch (type) { case 'fill': ctx.fill(); break; case 'stroke': ctx.stroke(); break; } ctx.restore();};

/** * 生成圆角图片 * @param ctx:canvas上下文 * @param img:图片实列 * @param x:坐标 * @param y:坐标 * @param w:宽 * @param h:高 * @param r:圆角 */interface Img { width: number; height: number;}const roundedRectangleImg = (ctx, img: Img, x: number, y: number, w: number, h: number, r = 0) => { ctx.save(); // 缩小倍数 const multiple = img.width / w; roundedRectangle(ctx, x, y, w, h, r); ctx.clip(); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, w, img.height / multiple); ctx.restore();};/** * @description: 绘制文字 * @param {*} ctx * @param {string} text * @param {*} fontSize * @return {*} */const writeText = (ctx, text: string, fontSize = 12, x = 0, y = 0, color = '#000') => { ctx.save(); ctx.font = `${fontSize}px serif`; ctx.fillStyle = color; ctx.textBaseline = 'middle'; ctx.translate(0, (fontSize / 2)); // 适配安卓 ios 下的文字居中问题 ctx.fillText(text, x, y); ctx.measureText(text); ctx.restore();};
复制代码
9.生成海报
  1. 解决屏幕分辨率不同产生的模糊问题

  2. 直接 canvas 渲染海报在 dom 节点上,不用再像以前的方式写两套(canvas,css)

  3. 没使用 htmlCanvas

  4. 可支持放大几倍屏幕的导出图片

注意:ios 最大支持 200W 像素(w*h),canvas 支持 500w

/** * @description: 生成圆角矩形 * @param {*} ctx * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @param {number} r * @param {string} color * @param {string} type:fill | stroke | undefined * @return void */const roundedRectangle = (ctx, x: number, y: number, w: number, h: number, r: number, color = '#000', type?: string | undefined) => {    const PI = Math.PI / 180;    ctx.translate(x, y);    ctx.save();    ctx.beginPath();    // ctx.moveTo(0, 0);    // ctx.lineTo(w, 0);
ctx.arc(w - r, r, r, PI * 270, PI * 360); // ctx.lineTo(w, h);
ctx.arc(w - r, h - r, r, PI * 0, PI * 90); // ctx.lineTo(0, h);
ctx.arc(r, h - r, r, PI * 90, PI * 180); // ctx.lineTo(0, 0);
ctx.arc(r, r, r, PI * 180, PI * 270); ctx.closePath(); ctx.fillStyle = color; switch (type) { case 'fill': ctx.fill(); break; case 'stroke': ctx.stroke(); break; } ctx.restore();};

/** * 生成圆角图片 * @param ctx:canvas上下文 * @param img:图片实列 * @param x:坐标 * @param y:坐标 * @param w:宽 * @param h:高 * @param r:圆角 */interface Img { width: number; height: number;}const roundedRectangleImg = ({ctx, img, x, y, w, h, r = 0}) => { ctx.save(); // 缩小倍数 const multiple = img.width / w; roundedRectangle(ctx, x, y, w, h, r); ctx.clip(); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, w, img.height / multiple); ctx.restore();};/** * @description: 绘制文字 * @param {*} ctx * @param {*}canvas * @param {string} text * @param {*} fontSize * @param {*} textSafeArea: 文字安全区域 * @return {*} */const writeText = (ctx, canvas, text: string, x = 0, y = 0, fontSize = 12, color = '#000', textSafeArea?) => { ctx.save(); ctx.font = `${fontSize}px serif`; ctx.fillStyle = color; ctx.textBaseline = 'middle'; ctx.textAlign = 'center';
ctx.translate(0, (fontSize / 2)); // 适配安卓 ios 下的文字居中问题 const textMsg: TextMetrics = ctx.measureText(text); if (canvas.width < textMsg.width || !!textSafeArea && textSafeArea < textMsg.width) { const newText = text.slice(0, text.length > 10 ? 10 : text.length); ctx.fillText(newText + '...', x, y); console.log(textMsg, 'textMsg', textMsg.width, canvas.width, text.slice(0, 10));
} else { ctx.fillText(text, x, y); } ctx.restore();};
interface CanvasCtx { canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D | null; postersEle: HTMLDivElement | null; cPx: number; cW: number; cH: number;}/** * @description: 初始化一画布 * @param {*} canvas:canvas实例 * @param {*} id:标签id * @param {*} w:canvas宽度 * @param {*} h:canvas高度 * @param {*} d:放大canvas倍数 * @return {} cPx:基于750计算出来的相对单位 */const initCanvas = ({ id, w = 0, h = 0, d = 1, canvas }): CanvasCtx => { // 获取设备像素比 const dPR = Math.round(window.devicePixelRatio); const postersEle: HTMLDivElement | null = document.querySelector(id); const screenW = window.screen.width; const screenH = window.screen.height; // 先获取节点下是否存在pl-canvas节点 const canvasName = '#pl-canvas'; const canvasDom: HTMLCanvasElement | null = document.querySelector(canvasName);
if (!canvasDom) { canvas = document.createElement('canvas'); canvas.setAttribute('id', 'pl-canvas'); canvas.style.width = '100%'; postersEle?.append(canvas); } const ctx: CanvasRenderingContext2D | null = canvas.getContext('2d'); const cW = (w || screenW) * d * dPR; const cH = (h || screenH) * d * dPR; canvas.width = cW; canvas.height = cH; const cPx = ((750 / 100) / (cW / 100) / d) * d; return { canvas, ctx, postersEle, cPx, cW, cH, };};/** * @description: 初始化一张图片实例 * @param {*} async * @return {*} */const loadingPictures = async (url: string): Promise<HTMLImageElement> => { return new Promise((resolve, reject) => { const img: HTMLImageElement = new Image(); img.src = url; img.onload = () => { resolve(img); }; img.onerror = (err) => { console.error(err, url); reject(null); }; });};/** * @description: 下载图片 * @param {string} url:下载地址 * @param {string} name:图片名称 * @return {*} */const downloadImage = (url: string, name?: string) => { const a: HTMLAnchorElement = document.createElement('a'); const event: MouseEvent = new MouseEvent('click'); a.download = name || 'photo'; a.href = url; a.dispatchEvent(event);};/** * @description: 文件生成临时地址 * @param {*} ArrayBuffer * @param {*} type * @return {*} */interface TemFileAddress { url: string; revokeObjectURL: () => void;}const temFileAddress = (file: Blob, type?: string): TemFileAddress => { const url = window.URL.createObjectURL(file); const revokeObjectURL = () => { window.URL.revokeObjectURL(url); }; return { url, revokeObjectURL, };};
复制代码


1.这是vue文件里面的主流程<template>      <div id='posters'>        <el-Button @click="generateImages"> img </el-Button>        <img :src="img" alt="">    </div></template>
<script lang='ts'>import { Component, Vue } from 'vue-property-decorator';import { roundedRectangleImg, convertBase64UrlToBlob, temFileAddress, writeText, initCanvas, loadingPictures, downloadImage } from '../units/canvas';
@Componentexport default class Posters extends Vue { url: string = require('../assets/1.jpg'); bg: string = require('../assets/p.png'); img: string | undefined = ''; canvas: HTMLCanvasElement | null = null; async mounted() { this.init(1); } async init(d): Promise<HTMLCanvasElement> { const { postersEle, ctx, cW, cPx, canvas } = initCanvas({id: '#posters', d, canvas: this.canvas }); this.canvas = canvas; !!postersEle && (postersEle.style.height = `${window.screen.width * d}px`); const bg = await loadingPictures(this.bg); const multiple = (bg.width / cW) * d; ctx?.drawImage(bg, 0, 0, cW, (bg.height / multiple) * d); const img: HTMLImageElement = await loadingPictures(this.url); roundedRectangleImg({ctx, img, x: 330 / cPx, y: 18 / cPx, w: 100 / cPx, h: 100 / cPx, r: 50 / cPx}); writeText(ctx, canvas, '我是文r上etttethdbtrdftrfdrthgdergegf', 380 / cPx, 128 / cPx, 18, '', 750 / cPx);
roundedRectangleImg( { ctx, img, x: 75 / cPx, y: 290 / cPx, w: 600 / cPx, h: 300 / cPx, r: 20 / cPx, }, ); ctx?.fillRect(0, 0, 100, 100); return canvas; }
async generateImages() { const canvas = await this.init(1); this.img = canvas?.toDataURL('image/png', 1); downloadImage(this.img); }}</script>
复制代码


发布于: 2021 年 05 月 14 日阅读数: 20
用户头像

关注

还未添加个人签名 2020.08.05 加入

还未添加个人简介

评论

发布
暂无评论
canvas从零到一,实际案例