uni-app(微信小程序)图片旋转放缩,文字绘制、海报绘制

总结一下:

要进行海报绘制离不开canvas,我们是先进行图片,文字的拖拽、旋转等操作
最后再对canvas进行绘制,完成海报绘制。

  1. 背景区域设置为 position: relative,方便图片在当前区域中拖动等处理。
  2. 添加图片,监听图片在背景区域下的 touchstart touchmove touchend 事件
  3. 拖动图片,在touchmove中,对图片进行位置(后续坐标-初始坐标)、角度(勾股定理计算点到圆心距离,利用角度计算公式计算)、放缩比例(勾股定理计算点到圆心的半径距离,拖动停止的半径除以初始的半径,获得放缩比例scale)的计算
  4. 最终canvas绘制,利用dom中的空canvas,将图片依次绘制到canvas中,并获取链接

部分主要代码如下:

const ctx = uni.createCanvasContext("myCanvas", this);
ctx.drawImage(this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);const ctx = uni.createCanvasContext("myCanvas", this);
ctx.drawImage(this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
ctx.save();
ctx.beginPath();// 画背景色(白色)
// ctx.setFillStyle('#fff');
// ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
for (let i=0; i<items.length; i++) {const cur = items[i]ctx.save();ctx.translate(0, 0);ctx.beginPath();if(cur.image) {ctx.translate(cur.x, cur.y); // 圆心坐标ctx.rotate(cur.angle * Math.PI / 180); // 图片旋转的角度ctx.translate(-(cur.width * cur.scale / 2), -(cur.height * cur.scale / 2)) // 图片的缩放ctx.drawImage(cur.image, 0, 0, cur.width * cur.scale, cur.height * cur.scale); // 图片绘制}if (cur.text) {ctx.font = `${cur.font}px arial`;ctx.fillStyle = cur.fillStyle;ctx.fillText(cur.text, cur.left, cur.top + Number(cur.font));console.log(cur.left, cur.top + Number(cur.font))}ctx.restore();
}
ctx.draw(true, () => {// 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (this.cutT / this.cropperH) * (this.imageH / pixelRatio)var canvasW = ((this.cropperW - this.cutL - this.cutR) / this.cropperW) * IMG_REAL_W;var canvasH = ((this.cropperH - this.cutT - this.cutB) / this.cropperH) * IMG_REAL_H;var canvasL = (this.cutL / this.cropperW) * IMG_REAL_W;var canvasT = (this.cutT / this.cropperH) * IMG_REAL_H;uni.canvasToTempFilePath({x: canvasL,y: canvasT,width: canvasW,height: canvasH,// destWidth: canvasW,// destHeight: canvasH,quality: +this.quality,fileType: this.fileType,canvasId: "myCanvas",success: (res) => {uni.hideLoading();// 成功获得地址的地方// this.$emit("getImg", res.tempFilePath);this.saveImg(res.tempFilePath)this.isShow = false;},fail: (err) => {uni.hideLoading();uni.showToast({title: "图片截取失败!",icon: "none",});},},this);
});
<!-- 海报背景区域,采用style动态调整,cropperInitW,cropperInitH一般为满屏 --><view class="uni-corpper":style="'width:' + cropperInitW + 'px;height:' + cropperInitH + 'px;background:#000'"><!-- 海报绘制区域,采用style动态调整,按照图片的长宽比例动态计算 cropperW等--><view class="uni-corpper-content" :style="'width:' +cropperW +'px;height:' +cropperH +'px;left:' +cropperL +'px;top:' +cropperT +'px'"><!-- 背景图片区域 cropperW等同上 --><image :src="imageSrc" :style="'width:' + cropperW + 'px;height:' + cropperH + 'px;' + 'border: 3px solid #ff0000;'"></image><!-- 海报上其他图片处理,for循环,itemList通过点击添加 --><block v-for="item in itemList" :key="item.id"><!-- 动态设置图片区域的缩放比例,还有pisition的左右位置,选中时z-index变大 --><view class='touchWrap' :style="{transform: 'scale(' + item.scale + ')', top: item.top + 'px', left: item.left + 'px', 'z-index':item.active ? 100 : 1}"><view class='imgWrap' :class="item.active ? 'touchActive' : ''" :style="{transform: 'rotate(' + item.angle + 'deg)', border: item.active ? 4 * item.oScale : 0 + 'rpx #fff dashed'}"><image v-if="item.image":src='item.image' :style="{width: item.width + 'px', height: item.height + 'px'}" <!-- 图片点击时,记录点击图片当前位置 -->@touchstart="(e) => WraptouchStart(e, item)"<!-- 图片拖动时,记录图片当前位置,并实时计算图片大小、旋转角度等,并存储至itemList中 -->@touchmove="(e) => WraptouchMove(e, item)"<!--一般不做处理 -->@touchend="(e) => WraptouchEnd(e, item)"mode="widthFix"></image><!-- 删除按钮 --><image class='x' src='/static/close.png' :style="{transform: 'scale(' + item.oScale + ')', 'transform-origin': center}"@click="(e) => deleteItem(e, item)"></image><!-- 图片放缩按钮 --><image v-if="item.image"class='o' src='/static/scale.png' :style="{transform: 'scale(' + item.oScale + ')', 'transform-origin': center}"<!-- 图片点击时,记录点击图片当前坐标,半径 -->@touchstart="(e) => oTouchStart(e, item)"<!-- 图片点击时,记录点击图片当前坐标,计算新的半径(得到scale缩放比例)计算角度差,获取当前角度 -->@touchmove="(e) => oTouchMove(e, item)"@touchend="(e) => WraptouchEnd(e, item)"></image></view></view></block></view><canvas canvas-id="myCanvas" :style="'position:absolute;border: 2px solid red; width:' +imageW +'px;height:' +imageH +'px;top:-9999px;left:-9999px;'"></canvas></view>
// 点击图片或文字WraptouchStart(e, it) {currentChoose = it// 循环图片数组获取点击的图片信息for (let i = 0; i < items.length; i++) {items[i].active = false;if (it.id == items[i].id) {index = i;items[index].active = true;}}// this.setData({//   itemList: items// })this.setList(items, 'itemList')// 获取点击的坐标值 lx ly是图片点击时的位置值items[index].lx = e.touches[0].clientX;items[index].ly = e.touches[0].clientY;},// 拖动图片WraptouchMove(e) {// 获取点击的坐标值 _lx _ly 是图片移动的位置值items[index]._lx = e.touches[0].clientX;items[index]._ly = e.touches[0].clientY;// left 是_lx 减去 lx,_ly 减去 ly,也就是现在位置,减去原来的位置。items[index].left += items[index]._lx - items[index].lx;items[index].top += items[index]._ly - items[index].ly;// 同理更新图片中心坐标点,用于旋转items[index].x += items[index]._lx - items[index].lx;items[index].y += items[index]._ly - items[index].ly;// 停止了以后,把lx的值赋值为现在的位置items[index].lx = e.touches[0].clientX;items[index].ly = e.touches[0].clientY;// this.setData({//   itemList: items// })this.setList(items, 'itemList')},// 放开图片WraptouchEnd(e, it) {touchNum ++clearTimeout(timer)timer = nulltimer = setTimeout(this.timeSta, 250)},// 计算坐标点到圆心的距离getDistancs(cx, cy, pointer_x, pointer_y) {var ox = pointer_x - cx;var oy = pointer_y - cy;return Math.sqrt(ox * ox + oy * oy);},/**参数cx和cy为图片圆心坐标*参数pointer_x和pointer_y为手点击的坐标*返回值为手点击的坐标到圆心的角度*/countDeg(cx, cy, pointer_x, pointer_y) {var ox = pointer_x - cx;var oy = pointer_y - cy;var to = Math.abs(ox / oy); // 勾股定理,计算当前点距离中心点的距离。var angle = Math.atan(to) / (2 * Math.PI) * 360; // 计算当前角度if (ox < 0 && oy < 0) //相对在左上角,第四象限,js中坐标系是从左上角开始的,这里的象限是正常坐标系  {angle = -angle;} else if (ox <= 0 && oy >= 0) //左下角,3象限  {angle = -(180 - angle)} else if (ox > 0 && oy < 0) //右上角,1象限  {angle = angle;} else if (ox > 0 && oy > 0) //右下角,2象限  {angle = 180 - angle;}return angle; // 返回角度},

体验:
自定义画报

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/76102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 Web HID API 的HID透传测试工具(纯前端)

前言 最近在搞HID透传 《STM32 USB使用记录&#xff1a;HID类设备&#xff08;后篇&#xff09;》 。 市面上的各种测试工具都或多或少存在问题&#xff0c;所以就自己写一个工具进行测试。目前来说纯前端方案编写这个工具应该是最方便的&#xff0c;这里放上相关代码。 项目…

数据库实现学生管理系统

1.QT将数据库分为三个层次&#xff1a; 1> 数据库驱动层&#xff1a;QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin 2> sql接口层&#xff1a;QSqlDatabase、QSqlQuery、QSqlRecord、QSqlError 3> 用户接口层&#xff1a;提供一些模型QSql…

linux非root安装特定版本的cuda

由于一些代码实现&#xff08;cuda写的外部扩展包&#xff09;对cuda版本要求比较高&#xff0c;因此&#xff0c;我在实验室linux系统下默认的cuda版本上&#xff0c;没办法编译扩展包。需要重新安装特定版本的cuda。 一. 首先&#xff0c;需要查看系统版本&#xff1a; lsb…

LabVIEW利用人工神经网络辅助进行结冰检测

LabVIEW利用人工神经网络辅助进行结冰检测 结冰对各个领域构成重大威胁&#xff0c;包括但不限于航空航天和风力涡轮机行业。在起飞过程中&#xff0c;飞机机翼上轻微积冰会导致升力降低25%。研究报告称&#xff0c;涡轮叶片上的冰堆积可在19个月的运行时间内造成29MWh的功率损…

【微服务部署】四、Jenkins一键打包部署NodeJS(Vue)前端项目步骤详解

本文介绍使用Jenkins一键将NodeJS&#xff08;Vue&#xff09;前端项目打包并上传到生产环境服务器&#xff0c;这里使用的是直接打包静态页面&#xff0c;发送到远程服务器Nginx配置目录的方式&#xff0c;首先确保服务器环境配置好&#xff0c;安装Nginx&#xff0c;运行目录…

解决Ubuntu无法安装pycairo和PyGObject

环境&#xff1a;虚拟机Ubuntu20.04&#xff0c;vscode无法安装pycairo和PyGObject 虚拟机Ubuntu20.04&#xff0c;vscode中运行Anaconda搭建的vens 的Python3.8.10 首先在vscode中点击ctrlshiftp&#xff0c;选择Python3.8.10的环境&#xff0c;自动激活Python 最近在搞无人…

第 362 场 LeetCode 周赛题解

A 与车相交的点 数据范围小直接暴力枚举 class Solution { public:int numberOfPoints(vector <vector<int>> &nums) {unordered_set<int> vis;for (auto &p: nums)for (int i p[0]; i < p[1]; i)vis.insert(i);return vis.size();} };B 判断能否…

普中 51 单片机点亮LED灯

普中 51 单片机 &#xff08;STC89C52RC&#xff09; LED / IO 将LED1进行闪烁操作 为啥要进行延时操作&#xff1f;依据人的肉眼余晖效应&#xff0c; 延时时间不能太短&#xff0c;否则就无法观察到 LED 闪烁 #include "reg52.h" typedef unsigned int u16; //对…

React中父子组件参数传递讲解

文章目录 结合案例&#xff1a;github搜索案例1.父容器代码2.搜索Search子模块代码3.展示Lisi子模块代码 父子参数传递分析1.子(Search)传父(App)2.父(App)传子(List) 结合案例&#xff1a;github搜索案例 案例结果展示如下图 1.父容器代码 import React, { Component } fr…

【GO语言基础】前言

系列文章目录 【Go语言学习】ide安装与配置 【GO语言基础】前言 【GO语言基础】变量常量 【GO语言基础】数据类型 文章目录 系列文章目录一、基础知识包和函数函数声明语法简洁性 括号成对出现GO常用DOS命令命名规则项目目录结构注释 总结 一、基础知识 包和函数 //声明本代…

Python中使用item()方法遍历字典的例子

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 这篇文章主要介绍了Python中使用item()方法遍历字典的例子, for…in这种是Python中最常用的遍历字典的方法了,需要的朋友可以参考下 Python字典的遍历方法有好几种&#xff0c;其中一种是for…in&#xff0c;这个我就…

记录docker 部署nessus

1、开启容器 docker run -itd --nameramisec_nessus -p 8834:8834 ramisec/nessus 2、登录 &#xff1a;注意是https https://ip8843 3、修改admin密码 #进入容器 docker exec -it ramisec_nessus /bin/bash#列出用户名 /opt/nessus/sbin/nessuscli lsuser#修改密码&a…

sqlserver union和union all 的区别

1.首先在数据库编辑1-40数字&#xff1b; 2.查询Num<30的数据&#xff0c;查询Num>20 and Num<40的数据&#xff0c;使用union all合并&#xff1b; 发现30-20的数字重复了&#xff0c;可见union all 不去重&#xff1b; 3.查询Num<30的数据&#xff0c;查询Num…

Promise 解决 Vue 中父子组件的加载问题!

前言 关于Promie我这里就不多解释了&#xff0c;不懂得可以看看官方文档。下面文章重点介绍项目中遇到的问题解决方法。 需求 组件b初始化某个用到的库&#xff0c;只有在初始化完成后才能调用其API&#xff0c;不然会报错。a页面负责调用。 // a.vue <template><d…

动态表单设计

动态表单设计 背景方案讨论基于上面分析&#xff0c;对比调研&#xff0c;自定义动态表单数据模型表单详解&#xff08;一&#xff09; 表单模板&#xff1a;jim_dynamic_form&#xff08;二&#xff09;表单数据类型&#xff1a;jim_form_data_type&#xff08;三&#xff09;…

【Linux】编辑器 vim

1、vim的基本概念 vi/vim【一款文本编辑器】vim【一款多模式编辑器】vi/vim 的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是 vim 是 vi 的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0…

分享一个python实验室设备预约管理系统 实验室设备维修系统源码 lw 调试

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

DAY08_MyBatisPlus——入门案例标准数据层开发CRUD-Lombok-分页功能DQL编程控制DML编程控制乐观锁快速开发-代码生成器

目录 一 MyBatisPlus简介1. 入门案例问题导入1.1 SpringBoot整合MyBatisPlus入门程序①&#xff1a;创建新模块&#xff0c;选择Spring初始化&#xff0c;并配置模块相关基础信息②&#xff1a;选择当前模块需要使用的技术集&#xff08;仅保留JDBC&#xff09;③&#xff1a;手…

《VulnHub》DarkHole:1

VulnHub 1&#xff1a;靶场信息2&#xff1a;打靶2.1&#xff1a;情报收集&威胁建模2.2&#xff1a;漏洞分析&渗透攻击 3&#xff1a;总结3.1&#xff1a;命令&工具3.1.1&#xff1a;Nmap 3.2&#xff1a;关键技术 VulnHub 打靶记录。官网&#xff1a;https://www.…

Android -BLE 蓝牙模块开发

Android-Ble蓝牙开发Demo示例–扫描,连接,发送和接收数据,分包解包(附源码) - 简书前言 万物互联的物联网时代的已经来临&#xff0c;ble蓝牙开发在其中扮演着举重若轻的角色。最近刚好闲一点&#xff0c;抽时间梳理下这块的知识点。 涉及ble蓝牙通讯的客户端(开启、扫描、连接…