svg 渲染 html 标签 及 添加到 canvas 中
主要用到 svg 的 foreignObject
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/foreignObject
记录demo
import { fabric } from "fabric"; import { Pieces } from "../@share/pieces"; /** * svg 将 html 内容转成 图片并添加到 canvas 中;支持改变并重新渲染 */ export class SvgForeignObject { width = '100px'; height = '50px'; styleObj = { 'font-weight': 'bold', 'background-color': 'blue', 'font-size': '16px', 'color': '#fff', 'border-radius': '5px', 'width': this.width, 'height': this.height, }; style = ''; // 最终使用的样式列表 innerHTML = 'Button'; constructor() { let body = $('body'); // fabric 获取 canvas 对象 let canvas = this.getCanvas(body); // 添加改变样式的操作按钮 this.addButtons(canvas, body); // 开始绘制 this.init(canvas, body); } /** * 流程整理成调用方法 * @param canvas * @param body */ init(canvas, body) { // 获取模板字符串 let str = this.getSvg(); // 将 svg 添加到 canvas 中 this.addSvgToCanvas(canvas, str); // 添加 svg 到 body,直观效果 this.addSvgToBody(body, str); } /** * 拼接 svg template * xmlns="http://www.w3.org/2000/svg" SVG专属命名空间 * <foreignObject>元素允许包含来自不同的 XML 命名空间的元素。在浏览器的上下文中,很可能是 XHTML / HTML。 * @return {string} */ getSvg() { // 拼接样式 this.style = '.btn {'; let attrs = Object.keys(this.styleObj); attrs.forEach(attr => { this.style = this.style + attr + ':' + this.styleObj[attr] + ';'; }); this.style += '}'; // 返回最终 svg str return ` <svg width="${ this.width }" height="${ this.height }" xmlns="http://www.w3.org/2000/svg"> <style> ${ this.style } </style> <foreignObject height="100%" width="100%"> <div class="btn" xmlns="http://www.w3.org/1999/xhtml" >${ this.innerHTML }</div> </foreignObject> </svg>`; } /** * fabric 获取 canvas 对象 * @param body * @return {*} */ getCanvas(body) { let id = new Date().getTime() + ''; body.append(`<canvas id="${ id }" width="500" height="500" style="border:1px solid grey;"></canvas>`); return new fabric.Canvas(id); } /** * 添加 svg 到 canvas 中,使用 dataUrl 形式 * @param canvas * @param str */ addSvgToCanvas(canvas, str) { let dataUrl = 'data:image/svg+xml;base64,' + window.btoa(str); new fabric.Image.fromURL(dataUrl, (img) => { let objs = canvas.getObjects(); if (objs.length) { // 存在则删除 canvas.remove(objs[0]); } canvas.add(img); img.center(); img.sendBackwards(); }); } /** * 添加改变样式的 按钮 * @param canvas * @param body */ addButtons(canvas, body) { body.append(`<button class="button">Change Style</button>`); $('.button').on('click', (e) => { this.styleObj['background-color'] = Pieces.getColor(); this.styleObj['color'] = Pieces.getColor(); this.innerHTML = 'Button' + Math.round(Math.random() * 10); // 重绘 this.init(canvas, body); }); } /** * svg 添加到 body 中 * @param body * @param str */ addSvgToBody(body, str) { if (document.getElementsByTagName('svg').length) { // 存在说明为更新,删除旧样式 document.getElementsByTagName('svg')[0].remove(); } body.append(str); } }
效果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现