二维码
微世推网

扫一扫关注

当前位置: 首页 » 企业商讯 » 热榜资讯 » 正文

微信小程序实现图片拖动_放大_缩小_旋转_滤镜和切图

放大字体  缩小字体 发布日期:2022-06-26 08:23:29    作者:高琪鱇    浏览次数:661
导读

示例简介感谢介绍使用感谢阅读小程序API+canvas来实现支持得可拖动、放大、缩小和旋转,并可对选中得支持进行不同效果得滤镜和不同形状得切图,且可对最终效果进行保存到本地。实现过程1、文件index.wxml和index.wxss代码如下,这一块比较简单,可自行查看,不做过多分析:view class='contentWarp'block wx:for=&#

示例简介

感谢介绍使用感谢阅读小程序API+canvas来实现支持得可拖动、放大、缩小和旋转,并可对选中得支持进行不同效果得滤镜和不同形状得切图,且可对最终效果进行保存到本地。

实现过程

1、文件index.wxml和index.wxss代码如下,这一块比较简单,可自行查看,不做过多分析:

<view class='contentWarp'> <block wx:for="{{itemList}}" wx:key="id"> <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 {{item.active? "touchActive":""}}' style="transform: rotate({{item.angle}}deg); border: {{item.active?4*item.oScale:0}}rpx #fff dashed;"> <image src=http://www.udxd.com/skin/m04redskin/image/nopic.gif data-id='{{item.id}}' style='width:{{item.width}}px;height:{{item.height}}px;' bindtouchstart='WraptouchStart' bindtouchmove='WraptouchMove' bindtouchend='WraptouchEnd' mode="widthFix"></image> <image class='x' src=http://www.udxd.com/skin/m04redskin/image/nopic.gif style='transform: scale({{item.oScale}});transform-origin:center;' data-id='{{item.id}}' bindtap='deleteItem'></image> <image class='o' src=http://www.udxd.com/skin/m04redskin/image/nopic.gif style='transform: scale({{item.oScale}});transform-origin:center;' data-id='{{item.id}}' bindtouchstart='oTouchStart' bindtouchmove='oTouchMove' bindtouchend='WraptouchEnd'></image> </view> </view> </block></view><!-- 右下角操作按钮 --><view class="operation-buttons"> <image src=http://www.udxd.com/skin/m04redskin/image/nopic.gif bindtap="uploadImg"></image> <image src=http://www.udxd.com/skin/m04redskin/image/nopic.gif bindtap="toggleFliters"></image> <image src=http://www.udxd.com/skin/m04redskin/image/nopic.gif bindtap="toggleShapes"></image> <image src=http://www.udxd.com/skin/m04redskin/image/nopic.gif bindtap="openMask"></image></view><!-- 各种过滤效果 --><view class="fliters" hidden="{{!showFliters}}"> <block wx:for="{{fliters}}" wx:key="id"> <image data-fliter="{{item.fliter}}" src=http://www.udxd.com/skin/m04redskin/image/nopic.gif bindtap="imgEffects"></image> </block></view><!-- 各种形状 --><view class="shapes" hidden="{{!showShapes}}"> <block wx:for="{{shapes}}" wx:key="id"> <image data-shape="{{item.shape}}" src=http://www.udxd.com/skin/m04redskin/image/nopic.gif bindtap="imgEffects"></image> </block></view><!-- 保存显示效果图 --><view class='canvasWrap' hidden="{{!showCanvas}}"> <image class="resImg" bindlongtap="saveImg" src=http://www.udxd.com/skin/m04redskin/image/nopic.gif mode="widthFix"></image> <view class='btn_view'> <button bindtap='saveImg'>保存到手机</button> <button bindtap='disappearCanvas'>关闭</button> </view></view><!-- 画布 --><canvas class='maskCanvas' canvas-id="maskCanvas" style='width:{{canvasWidth}}px; height:{{canvasHeight}}px;'></canvas>

.contentWarp { position: absolute; width: 百分百; height: 百分百; top: 0; left: 0; bottom: 0; right: 0; margin: auto;}.touchWrap { transform-origin: center; position: absolute; z-index: 100;}.imgWrap { box-sizing: border-box; width: 百分百; transform-origin: center; float: left; border: 5rpx transparent dashed;}.imgWrap image { float: left;}.touchActive .x { display: block;}.touchActive .o { display: block;}.x { position: absolute; top: -25rpx; left: -25rpx; z-index: 500; display: none; width: 50rpx; height: 50rpx; overflow: hidden; font-weight: bold; color: #d1e3f1;}.o { position: absolute; bottom: -25rpx; right: -25rpx; width: 50rpx; height: 50rpx; text-align: center; display: none; overflow: hidden; font-weight: bold; color: #d1e3f1;}.active { background-color: rgb(78, 114, 151);}.active view { border: none;}.touchActive { z-index: 400;}.operation-buttons { position: absolute; bottom: 100rpx; right: 20rpx; display: flex; flex-direction: column; z-index: 101;}.operation-buttons image { width: 100rpx; height: 100rpx; margin-top: 40rpx;}.canvasWrap { position: absolute; width: 百分百; height: 百分百; top: 0; left: 0; background-color: rgba(0, 0, 0, 0.6); z-index: 999; text-align: center;}.maskCanvas { position: absolute; left: -200%; top: 0;}.btnView view { padding-bottom: 20rpx;}.hand { position: absolute; left: 100rpx; right: 0; margin: auto; z-index: 100;}.getUserInfoBtn { position: initial; border: none; background-color: none;}.getUserInfoBtn::after { border: none;}.btn_view { display: flex; padding: 20rpx;}.btn_view button { width: 210rpx; font-size: 28rpx; background-color: #eb4985; color: #fff; line-height: 80rpx;}.resImg { width: 75%; margin-top: 10px;}.fliters { display: flex; flex-direction: column; position: absolute; bottom: 382rpx; right: 120rpx; z-index: 201;}.shapes { display: flex; flex-direction: column; position: absolute; bottom: 242rpx; right: 120rpx; z-index: 201;}.fliters image, .shapes image { width: 60rpx; height: 60rpx; border: 2rpx solid #eb4985;}

2、文件index.js存放所有功能得逻辑代码,相对比较复杂,下面分开来分析几个重点方法:

1)方法uploadImg+setDropItem:获取上传支持得信息,跟设置得蕞大宽高进行判断(maxWidth, maxHeight),然后根据判断得结果进行缩放,避免大图溢出,且设置支持得地址、坐标、定位和是否选中等信息;用于后面功能使用,支持多图使用;

2)方法WraptouchStart+WraptouchMove:获取支持移动坐标和触发时坐标得差值,加上支持本来得坐标来实现移动效果,注意要把移动坐标赋值给触发时坐标(items[index].lx = e.touches[0].clientX),不然会导致移动有问题;

3)方法oTouchStart+oTouchMove:获取拖动后支持得半径跟触发时支持得半径得比值,再使用scale来实现缩放功能(items[index].disPtoO / items[index].r);获取触发时得支持角度+拖动后支持得角度,再使用rotate来实现旋转功能(items[index].angle = items[index].rotate);

4)方法imgEffects:调用滤镜方法util.imgFliters(详细可到感谢分享jingyan.baidu感谢原创分享者/article/ed15cb1b9fd9bb1be3698183.html查看),根据设置得滤镜值,进行不同得滤镜处理;而调用形状方法util.imgShapes,根据设置得形状值,进行不同得切图效果;

5)方法synthesis:用来把所有支持得最终效果合成一个画布,用于保存支持到本地使用;

6)方法saveImg:把画布保存到本地相册。

let index = 0, // 当前感谢阅读支持得index items = [], // 支持数组信息 itemId = 1, // 支持id,用于识别感谢阅读支持 fliter = 'init', // 默认过滤类型(原图) shape = 'init'; // 默认形状(原图)const hCw = 1.62; // 支持宽高比const canvasPre = 1; // 展示得canvas占mask得百分比const maskCanvas = wx.createCanvasContext('maskCanvas', this); // 创建 canvas 得绘图上下文 CanvasContext 对象const util = require('../../utils/util.js');Page({ data: { itemList: [], showFliters: false, // 默认不显示过滤效果框 showShapes: false, // 默认不显示形状效果框 fliters: [{ fliter: 'init', src: '../../images/init.jpg' }, { fliter: 'bw', src: '../../images/bw.jpg' }, { fliter: 'groundGlass', src: '../../images/groundGlass.jpg' }, { fliter: 'pictureStory', src: '../../images/pictureStory.jpg' }, { fliter: 'reminiscence', src: '../../images/reminiscence.jpg' }, { fliter: 'sketch', src: '../../images/sketch.jpg' }], shapes: [{ shape: 'circle', src: '../../images/init.jpg' }, { shape: 'star', src: '../../images/init.jpg' }, { shape: 'irregularityHeart', src: '../../images/init.jpg' }, { shape: 'SudokuHeart', src: '../../images/init.jpg' }] }, onLoad: function(options) { items = this.data.itemList; wx.getSystemInfo({ // 获取系统信息 success: sysData => { this.sysData = sysData // 设置画布宽高,this.sysData.windowWidth为屏幕得宽度 this.setData({ canvasWidth: this.sysData.windowWidth * canvasPre, // 如果觉得不清晰得话,可以把所有组件、宽高放大一倍 canvasHeight: this.sysData.windowWidth * canvasPre * hCw, }) } }) }, // 上传支持 uploadImg() { let that = this; wx.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success (res) { // tempFilePath可以作为img标签得src属性显示支持 that.setDropItem({ url: res.tempFilePaths[0] }); } }) }, // 设置支持得信息 setDropItem(imgData) { let data = {}; // 存储支持信息 // 获取支持信息,网络支持需先配置download域名才能生效 wx.getImageInfo({ src: imgData.url, success: res => { // 初始化数据 let maxWidth = 150, maxHeight = 150; // 设置蕞大宽高 if (res.width > maxWidth || res.height > maxHeight) { // 原图宽或高大于蕞大值就执行 if (res.width / res.height > maxWidth / maxHeight) { // 判断比例使用蕞大值得宽或高作为基数计算 data.width = maxWidth; data.height = Math.round(maxWidth * (res.height / res.width)); } else { data.height = maxHeight; data.width = Math.round(maxHeight * (res.width / res.height)); } } data.image = imgData.url; // 显示地址 data.initImage = imgData.url; // 原始地址 data.id = ++itemId; // id data.top = 0; // top定位 data.left = 0; // left定位 // 圆心坐标 data.x = data.left + data.width / 2; data.y = data.top + data.height / 2; data.scale = 1; // scale缩放 data.rotate = 1; // 旋转角度 data.active = false; // 选中状态 items[items.length] = data; // 每增加一张支持数据增加一条信息 this.setData({ itemList: items }) } }) }, // 感谢阅读支持 WraptouchStart: function(e) { // 循环支持数组获取感谢阅读得支持信息 for (let i = 0; i < items.length; i++) { items[i].active = false; if (e.currentTarget.dataset.id == items[i].id) { index = i; items[index].active = true; } } this.setData({ itemList: items }) // 获取感谢阅读得坐标值 items[index].lx = e.touches[0].clientX; items[index].ly = e.touches[0].clientY; }, // 拖动支持 WraptouchMove(e) { items[index]._lx = e.touches[0].clientX; items[index]._ly = e.touches[0].clientY; 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; items[index].lx = e.touches[0].clientX; items[index].ly = e.touches[0].clientY; this.setData({ itemList: items }) }, // 放开支持 WraptouchEnd() { this.synthesis(); // 调用合成图方法 }, // 感谢阅读伸缩图标 oTouchStart(e) { //找到感谢阅读得那个支持对象,并记录 for (let i = 0; i < items.length; i++) { items[i].active = false; if (e.currentTarget.dataset.id == items[i].id) { index = i; items[index].active = true; } } //获取作为移动前角度得坐标 items[index].tx = e.touches[0].clientX; items[index].ty = e.touches[0].clientY; //移动前得角度 items[index].anglePre = this.countDeg(items[index].x, items[index].y, items[index].tx, items[index].ty); //获取支持半径 items[index].r = this.getDistancs(items[index].x, items[index].y, items[index].left, items[index].top); }, oTouchMove: function(e) { //记录移动后得位置 items[index]._tx = e.touches[0].clientX; items[index]._ty = e.touches[0].clientY; //移动得点到圆心得距离 items[index].disPtoO = this.getDistancs(items[index].x, items[index].y, items[index]._tx, items[index]._ty - 10) items[index].scale = items[index].disPtoO / items[index].r; //移动后位置得角度 items[index].angleNext = this.countDeg(items[index].x, items[index].y, items[index]._tx, items[index]._ty) //角度差 items[index].new_rotate = items[index].angleNext - items[index].anglePre; //叠加得角度差 items[index].rotate += items[index].new_rotate; items[index].angle = items[index].rotate; //赋值 //用过移动后得坐标赋值为移动前坐标 items[index].tx = e.touches[0].clientX; items[index].ty = e.touches[0].clientY; items[index].anglePre = this.countDeg(items[index].x, items[index].y, items[index].tx, items[index].ty) //赋值setData渲染 this.setData({ itemList: items }) }, // 计算坐标点到圆心得距离 getDistancs(cx, cy, pointer_x, pointer_y) { var ox = pointer_x - cx; var oy = pointer_y - cy; return Math.sqrt( ox * ox + oy * oy ); }, countDeg: function(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; }, deleteItem: function(e) { let newList = []; for (let i = 0; i < items.length; i++) { if (e.currentTarget.dataset.id != items[i].id) { newList.push(items[i]) } } if (newList.length > 0) { newList[newList.length - 1].active = true; // 剩下支持组最后一个选中 } items = newList; this.setData({ itemList: items }) }, // 打开遮罩层 openMask() { this.synthesis(); this.setData({ showCanvas: true }) }, synthesis() { // 合成支持 maskCanvas.save(); maskCanvas.beginPath(); // 画背景色(白色) maskCanvas.setFillStyle('#fff'); maskCanvas.fillRect(0, 0, this.data.canvasWidth, this.data.canvasHeight); items.forEach((currentValue, index) => { maskCanvas.save(); maskCanvas.translate(0, 0); maskCanvas.beginPath(); maskCanvas.translate(currentValue.x, currentValue.y); // 圆心坐标 maskCanvas.rotate(currentValue.angle * Math.PI / 180); maskCanvas.translate(-(currentValue.width * currentValue.scale / 2), -(currentValue.height * currentValue.scale / 2)) maskCanvas.drawImage(currentValue.image, 0, 0, currentValue.width * currentValue.scale, currentValue.height * currentValue.scale); maskCanvas.restore(); }) // reserve 参数为 false,则在本次调用绘制之前 native 层会先清空画布再继续绘制 maskCanvas.draw(false, (e) => { wx.canvasToTempFilePath({ canvasId: 'maskCanvas', success: res => { this.setData({ canvasTemImg: res.tempFilePath }) } }, this); }) }, // 感谢阅读切换显示过滤框 toggleFliters() { this.setData({ showFliters: !this.data.showFliters, showShapes: false }); }, // 感谢阅读切换显示形状框 toggleShapes() { this.setData({ showShapes: !this.data.showShapes, showFliters: false }); }, // 支持特效 imgEffects(e) { fliter = e.currentTarget.dataset.fliter || 'init'; shape = e.currentTarget.dataset.shape || 'init'; let that = this; items.forEach((currentValue, index) => { if (currentValue.active) { maskCanvas.save(); maskCanvas.beginPath(); util.imgShapes(maskCanvas, 0, 0, currentValue.width, currentValue.width, shape, 0, currentValue); // 支持剪切不同形状 maskCanvas.clearRect(0, 0, currentValue.width, currentValue.height); maskCanvas.drawImage(currentValue.initImage, 0, 0, currentValue.width, currentValue.height); maskCanvas.draw(false, function() { wx.canvasGetImageData({ // 获取canvas区域得像素数据 canvasId: 'maskCanvas', x: 0, y: 0, width: currentValue.width, height: currentValue.height, success(res) { let imageData = res.data; util.imgFliters(maskCanvas, fliter, res); // 调用支持滤镜函数 maskCanvas.clearRect(0, 0, currentValue.width, currentValue.height); // 清除旧得,不然会导致卡顿 maskCanvas.restore(); wx.canvasPutImageData({ // 将像素数据绘制到canvas canvasId: 'maskCanvas', x: 0, y: 0, width: currentValue.width, height: currentValue.height, data: imageData, success(res) { wx.canvasToTempFilePath({ canvasId: 'maskCanvas', width: currentValue.width, height: currentValue.height, destWidth: currentValue.width, destHeight: currentValue.height, success: res => { items[index].image = res.tempFilePath that.setData({ itemList: items }) } }, this) } }) } }); }) }; }) }, // 关闭遮罩层 disappearCanvas() { this.setData({ showCanvas: false }) }, // 保存支持到系统相册 saveImg: function() { wx.saveImageToPhotosAlbum({ filePath: this.data.canvasTemImg, success: res => { wx.showToast({ title: '保存成功', icon: "success" }) }, fail: res => { wx.openSetting({ success: settingdata => { if (settingdata.authSetting['scope.writePhotosAlbum']) { console.log('获取权限成功,给出再次感谢阅读支持保存到相册得提示。') } else { console.log('获取权限失败,给出不给权限就无法正常使用得提示') } }, fail: error => { console.log(error) } }) wx.showModal({ title: '提示', content: '保存失败,请确保相册权限已打开', }) } }) }})

 
(文/高琪鱇)
打赏
免责声明
• 
本文为高琪鱇原创作品•作者: 高琪鱇。欢迎转载,转载请注明原文出处:http://www.udxd.com/qysx/show-127132.html 。本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们邮件:weilaitui@qq.com。
 

Copyright©2015-2023 粤公网安备 44030702000869号

粤ICP备16078936号

微信

关注
微信

微信二维码

WAP二维码

客服

联系
客服

联系客服:

24在线QQ: 770665880

客服电话: 020-82301567

E_mail邮箱: weilaitui@qq.com

微信公众号: weishitui

韩瑞 小英 张泽

工作时间:

周一至周五: 08:00 - 24:00

反馈

用户
反馈