• canvas实现画布拖拽效果 适配Uniapp和Vue (开箱即用)


    需求:我司是做AIGC项目最近和地铁项目有关需要实现海报效果图,并且需要使用画布拖拽和修改上传删除等等功能 当时连续加班花了10个工作日搓出来 实现挺简单的但是Canvas数据处理还是挺麻烦的  大概功能如图下 

    首先我们需要引入Fabric.js 这个库封装好了原生的Canvas的Api使用uniapp和vue项目

    Fabric.js能做的事情
    • 在Canvas上创建、填充图形(包括图片、文字、规则图形和复杂路径组成图形)。
    • 给图形填充渐变颜色。
    • 组合图形(包括组合图形、图形文字、图片等)。
    • 设置图形动画集用户交互。
    • 生成JSON, SVG数据等。
    • 生成Canvas对象自带拖拉拽功能。

     步骤一:引入Fabric.js库

    npm i fabric --save

    步骤二:安装less或者sass因为需要更改canvas样式需要用到deep(使用sass注意更改style的lang)

    npm i less 
    npm i less-loader

    步骤三:创建DOM节点 uniapp和纯vue项目的canvas创建是不一样的 (巨坑)

    Uniapp的canvas外层会嵌套uniapp-canvas会阻碍到Fabric.js读取但是纯vue项目则不会

    因为Canvas代码量比较多 我这边把坑就额外标记出来 建议复制到vscode查看更佳 (下列代码复制即可使用 亲测有效 一定要按照步骤来)

    1. <script>
    2. import {
    3. fabric
    4. } from 'fabric'; // 引入库
    5. let canvas = null // 最好设置成全局的不要定义到data里面
    6. export default {
    7. data() {
    8. return {
    9. }
    10. },
    11. mounted() {
    12. this.createCanvasDom('card-canvas') // 初始化节点
    13. this.init() //初始化画布
    14. },
    15. methods: {
    16. // 创建画布
    17. createCanvasDom(elID) {
    18. var cardContent = document.getElementById('card-canvas-content'); //选取父节点
    19. var cardCanvas = document.createElement('canvas'); // 创建canvas标签
    20. cardCanvas.setAttribute('id', elID); // 设置canvas的ID
    21. cardContent.appendChild(cardCanvas); // 插入canvas到父节点中
    22. },
    23. // 初始化画布
    24. init() {
    25. // 画布初始配置
    26. canvas = new fabric.Canvas('card-canvas', { // 获取画布节点传入 初始化节点ID
    27. width: 200, // 后续可以更改
    28. height: 200,
    29. });
    30. }
    31. }
    32. }
    33. script>
    34. <style lang="less" scoped>
    35. /deep/ #card-canvas{
    36. border: 1px solid red ;
    37. }
    38. style>

     步骤四:canvas基础配置(这些配置是可选的可以根据项目情况自行配置)

    1. // 初始化画布
    2. init() {
    3. // 画布初始配置
    4. canvas = new fabric.Canvas('card-canvas', { // 获取画布节点传入 初始化节点ID
    5. width: 200, // 后续可以更改
    6. height: 200,
    7. });
    8. fabric.Object.prototype.padding = 10;
    9. fabric.Object.prototype.controls.mtr.withConnection = false;
    10. // 解决动态生成文字模糊问题
    11. fabric.Object.prototype.objectCaching = false;
    12. fabric.Object.prototype.borderColor = 'dodgerblue';
    13. // 修改控制点的形状,默认为`rect`矩形,可选的值还有`circle`圆形
    14. fabric.Object.prototype.cornerStyle = 'circle';
    15. // 修改控制点的填充色为白色
    16. fabric.Object.prototype.cornerColor = 'white';
    17. // 修改控制点的大小为10px
    18. fabric.Object.prototype.cornerSize = 10;
    19. // 设置控制点不透明,即可以盖住其下的控制线
    20. fabric.Object.prototype.transparentCorners = false;
    21. // 修改控制点的边框颜色为`gray`灰色
    22. fabric.Object.prototype.cornerStrokeColor = 'gray';
    23. // 单独修改旋转控制点距离主体的纵向距离为-20px
    24. fabric.Object.prototype.controls.mtr.offsetY = -20;
    25. // 单独修改旋转控制点,光标移动到该点上时的样式为`pointer`,一个手的形状
    26. fabric.Object.prototype.controls.mtr.cursorStyle = 'pointer';
    27. }

    第五步:初始化数据建议抽离一个方法出去避免都写在init里面过于冗杂 (在init里面调用放置末尾

    1. init() {
    2. this.initDate()
    3. }
    1. // 初始化数据
    2. initDate() {
    3. //更改画布后续宽高 可以根据后台的图片动态设置canvas大小
    4. canvas.setDimensions({
    5. width: this.width,
    6. height: this.height
    7. });
    8. // fabric内置的方法 用于重新渲染canvas 在字体以及大小更换 图片替换都需要调用此方法 否则canvas不会更新新数据
    9. canvas.renderAll();
    10. this.$nextTick(() => {
    11. // canvas渲染完成之后最后就会执行一般是处理loading效果的
    12. });
    13. }

    步骤六:导入背景图 (巨坑 当时我研究了贼久 一直报错图片污染导致后续图片无法导出canvas)  

    我们需要把远程的背景图片转成base64的格式不然导出canvas会报错 这也是跨域 网上有很多解决方案都没用

    代码比较长我就抽离出去写注释 这些方法统一在initDate里面调用 

    1. // 图片转码
    2. getURLBase64(url) {
    3. //将远程图片下载本地成为base64图片
    4. let _this = this;
    5. return new Promise((resolve, reject) => {
    6. var xhr = new XMLHttpRequest();
    7. xhr.open('get', url, true);
    8. xhr.responseType = 'blob';
    9. xhr.onload = function() {
    10. if (this.status === 200) {
    11. var blob = this.response;
    12. var fileReader = new FileReader();
    13. fileReader.onloadend = function(e) {
    14. var result = e.target.result;
    15. _this.imageUrl = result;
    16. resolve(result);
    17. };
    18. fileReader.readAsDataURL(blob);
    19. }
    20. };
    21. xhr.onerror = function() {
    22. reject();
    23. };
    24. xhr.send();
    25. });
    26. },

     设置Canvas背景图片

    1. // 设置/修改画布背景图 bgUrl:转码后的背景图片 canvasDOM:canvas的实例变量名
    2. setBackgroundImage(bgUrl, canvasDOM) {
    3. canvasDOM.setBackgroundImage(bgUrl, canvasDOM.renderAll.bind(canvasDOM), { // canvas实例变量
    4. originX: 'left',
    5. originY: 'top',
    6. left: 0,
    7. top: 0,
    8. level: 1, // 层级
    9. bgUrl // 路径
    10. });
    11. },

     使用:

    1. async initDate() {
    2. //更改画布后续宽高 可以根据后台的图片动态设置canvas大小
    3. canvas.setDimensions({
    4. width: this.width,
    5. height: this.height
    6. });
    7. // fabric内置的方法 用于重新渲染canvas 在字体以及大小更换 图片替换都需要调用此方法 否则canvas不会更新新数据
    8. canvas.renderAll();
    9. // 背景地址 自己随便填一个线上的即可
    10. let bgUrl = await this.getURLBase64('https://img1.baidu.com/it/u=395218774,2205974617&fm=253&app=138&size=w931&n=0&f=GIF&fmt=auto?sec=1718730000&t=15d143401501c32f552f4157563acaf4');
    11. // 设置背景图片
    12. this.setBackgroundImage(bgUrl, canvas)
    13. },

     此时是页面呈现的样式

     

    步骤六:插入文字

    1. // 插入文字 text:文本 canvasDOM:canvas的实例变量名
    2. setTextFont(text,canvasDOM) {
    3. const textDOM = new fabric.Textbox(text, {
    4. top: 0, // 位置
    5. left: 0,
    6. fontSize: 19, // 文字大小
    7. fill: 'red', // 颜色
    8. level: 0, // 层级
    9. fontFamily: '思源黑体 CN Heavy',
    10. splitByGrapheme: true
    11. });
    12. canvasDOM.add(textDOM); //添加文字
    13. canvasDOM.bringToFront(textDOM); //优先级
    14. }

    使用

    1. async initDate() {
    2. //更改画布后续宽高 可以根据后台的图片动态设置canvas大小
    3. canvas.setDimensions({
    4. width: this.width,
    5. height: this.height
    6. });
    7. // fabric内置的方法 用于重新渲染canvas 在字体以及大小更换 图片替换都需要调用此方法 否则canvas不会更新新数据
    8. canvas.renderAll();
    9. // 背景地址 自己随便填一个线上的即可
    10. let bgUrl = await this.getURLBase64('https://img1.baidu.com/it/u=395218774,2205974617&fm=253&app=138&size=w931&n=0&f=GIF&fmt=auto?sec=1718730000&t=15d143401501c32f552f4157563acaf4');
    11. // 设置背景图片
    12. this.setBackgroundImage(bgUrl, canvas)
    13. // 插入文本
    14. this.setTextFont('这是一行文本',canvas)
    15. },

     页面效果 此时页面的文字是可以进行拖拽等操作的

     

    (内容有点多 先更新三分之一 后面再抽空更新)

  • 相关阅读:
    使用AutoJs编写UI的踩坑记录
    Python学习之——测试环境路径的工程示例
    最长公共子序列
    josn模块和ujson模块性能对比
    Java 面试 知识清单
    【另类加法】
    视频通话中的Camera操作
    SSM+医保业财一体化管理系统 毕业设计-附源码151023
    .NET周报 【3月第1期 2023-03-03】
    【JavaEE进阶系列 | 从小白到工程师】Math类与System类的常用方法使用
  • 原文地址:https://blog.csdn.net/2303_77072102/article/details/139738226