parent
884ce456d5
commit
6bdc61c950
@ -0,0 +1,27 @@ |
||||
|
||||
## 3.0.1(2022-11-03) |
||||
修复 撤销和重做不生效的问题 |
||||
## 3.0.0(2022-11-03) |
||||
使用wxs重构代码,性能大提升 |
||||
新增 支持蒙版裁剪,可以裁剪任何形状的图形(详情见demo示例) |
||||
新增 支持在弹窗中使用(详情见demo示例) |
||||
移除 由于插槽会导致许多问题,实际上开发者自己封装组件反而更简单,所以3.0版本以后移除插槽,2.0迁移教程见 demo:全屏裁剪 |
||||
## 2.0.3(2022-08-21) |
||||
修复 在vue3 程序中报错的问题 |
||||
新增 新增了图片初始化完成和加载失败的事件 |
||||
## 2.0.2(2022-08-18) |
||||
新增 增加了原像素裁剪功能,即使用用户在裁剪框取景的大小作为输出像素,换句话说,输出的图片分辨率与输入图片分辨率一样 |
||||
新增 增加了change事件,会在图像和裁剪框位置变化后触发 |
||||
新增 增加了compress参数 压缩图片,压缩图片是为了提升流畅度,所以只会针对用户拖动的那张图片进行压缩,最终输出的图像品质并不会受到影响 |
||||
修复 用户在没有传入图像时报错的问题 |
||||
修复 ios在某些机型上拖动出现残留的问题 |
||||
## 2.0.1(2022-07-20) |
||||
修复:ios打包成app的时候有几率裁剪不成功的问题 |
||||
## 2.0.0(2022-07-13) |
||||
更新了2.0版本,增加了图片放大功能 |
||||
## bt-cropper 图片裁切 |
||||
======== |
||||
### 2022年7月13日 发布2.0版本 |
||||
* 1.完全重构了代码,并且优化了性能 |
||||
* 2.支持app |
||||
* 3.修复demo在H5的情况下高度错误的问题 |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@ |
||||
@font-face { |
||||
font-family: "iconfont"; /* Project id 3311610 */ |
||||
src: url('//at.alicdn.com/t/font_3311610_7wh8injedpd.woff2?t=1649382821379') format('woff2'), |
||||
url('//at.alicdn.com/t/font_3311610_7wh8injedpd.woff?t=1649382821379') format('woff'), |
||||
url('//at.alicdn.com/t/font_3311610_7wh8injedpd.ttf?t=1649382821379') format('truetype'); |
||||
} |
||||
|
||||
.iconfont { |
||||
font-family: "iconfont" !important; |
||||
font-size: 16px; |
||||
font-style: normal; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
} |
||||
|
||||
.icon-reset:before { |
||||
content: "\e611"; |
||||
} |
||||
|
||||
.icon-move:before { |
||||
content: "\e67b"; |
||||
} |
@ -0,0 +1,225 @@ |
||||
var startTouchs = []; |
||||
var touchType = '' |
||||
var startDistance = 0; |
||||
var touchCenter = []; |
||||
var cropperRect = null; |
||||
var imageRect = null; |
||||
var directionX = 0; |
||||
var directionY = 0; |
||||
var ratio = 0; |
||||
// 操作时改变的对象
|
||||
var changes = { |
||||
imageRect: null, |
||||
cropperRect: null |
||||
} |
||||
export default { |
||||
computed: { |
||||
imageStyle() { |
||||
const imageRect = this.imageRect |
||||
if (imageRect) { |
||||
return { |
||||
left: imageRect.left + 'px', |
||||
top: imageRect.top + 'px', |
||||
width: imageRect.width + 'px', |
||||
height: imageRect.height + 'px' |
||||
} |
||||
} else { |
||||
return {} |
||||
} |
||||
}, |
||||
cropperStyle() { |
||||
const cropperRect = this.cropperRect |
||||
if (cropperRect) { |
||||
return { |
||||
left: cropperRect.left + 'px', |
||||
top: cropperRect.top + 'px', |
||||
width: cropperRect.width + 'px', |
||||
height: cropperRect.height + 'px' |
||||
} |
||||
} else { |
||||
return {} |
||||
} |
||||
} |
||||
}, |
||||
methods: { |
||||
touchStart() { |
||||
let ev; |
||||
if (arguments.length == 3) { |
||||
directionX = arguments[0]; |
||||
directionY = arguments[1]; |
||||
ev = arguments[2]; |
||||
touchType = "controller"; |
||||
} else { |
||||
touchType = "image"; |
||||
ev = arguments[0]; |
||||
} |
||||
startTouchs = ev.touches; |
||||
changes = { |
||||
imageRect: this.imageRect, |
||||
cropperRect: this.cropperRect |
||||
}; |
||||
ratio = this.ratio; |
||||
cropperRect = { |
||||
...changes.cropperRect |
||||
} |
||||
imageRect = { |
||||
...changes.imageRect |
||||
} |
||||
if (startTouchs.length == 2) { |
||||
const imageRect = this.imageRect |
||||
var x1 = startTouchs[0].clientX |
||||
var y1 = startTouchs[0].clientY |
||||
var x2 = startTouchs[1].clientX |
||||
var y2 = startTouchs[1].clientY |
||||
var distance = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) |
||||
startDistance = Math.sqrt(distance) |
||||
var leftPercent = ((x1 + x2) / 2 - imageRect.left) / imageRect.width |
||||
var topPercent = ((y1 + y2) / 2 - imageRect.top) / imageRect.height |
||||
touchCenter = [leftPercent, topPercent] |
||||
} |
||||
}, |
||||
touchMove(ev) { |
||||
if(startTouchs.length!==ev.touches.length) return |
||||
var touches = ev.touches; |
||||
var changeX1 = touches[0].clientX - startTouchs[0].clientX; |
||||
var changeY1 = touches[0].clientY - startTouchs[0].clientY; |
||||
if (startTouchs.length == 1) { |
||||
if (touchType === 'image') { |
||||
changes.imageRect.left = imageRect.left + changeX1; |
||||
changes.imageRect.top = imageRect.top + changeY1; |
||||
// console.log(startTouchs.length,ev.touches.length)
|
||||
} else if (touchType === 'controller') { |
||||
var changeX = changeX1 * directionX; |
||||
var changeY = changeY1 * directionY; |
||||
// 比例缩放控制
|
||||
if (ratio !== 0) { |
||||
if (directionX * directionY !== 0) { |
||||
if (changeX / ratio > changeY) { |
||||
changeY = changeX / ratio |
||||
changeX = changeY * ratio |
||||
} else { |
||||
changeX = changeY * ratio |
||||
changeY = changeX / ratio |
||||
} |
||||
} else { |
||||
if (directionX == 0) { |
||||
changeX = changeY * ratio |
||||
} else { |
||||
changeY = changeX / ratio |
||||
} |
||||
} |
||||
} |
||||
|
||||
var width = cropperRect.width + changeX |
||||
var height = cropperRect.height + changeY |
||||
var imageRight = imageRect.left + imageRect.width |
||||
var imageBottom = imageRect.top + imageRect.height |
||||
if (directionX != -1) { |
||||
if (cropperRect.left + width > imageRight) { |
||||
width = imageRight - cropperRect.left |
||||
if (ratio !== 0) { |
||||
height = width / ratio |
||||
} |
||||
} |
||||
} else { |
||||
var cLeft = cropperRect.left - changeX |
||||
if (cLeft < imageRect.left) { |
||||
width = cropperRect.left + cropperRect.width - imageRect.left |
||||
if (ratio !== 0) { |
||||
height = width / ratio |
||||
} |
||||
} |
||||
} |
||||
// 判断是否触底
|
||||
if (directionY != -1) { |
||||
if (cropperRect.top + height > imageBottom) { |
||||
height = imageBottom - cropperRect.top |
||||
if (ratio !== 0) { |
||||
width = height * ratio |
||||
} |
||||
} |
||||
} else { |
||||
var cTop = cropperRect.top - changeY |
||||
if (cTop < imageRect.top) { |
||||
height = cropperRect.top + cropperRect.height - imageRect.top |
||||
if (ratio !== 0) { |
||||
width = height * ratio |
||||
} |
||||
} |
||||
} |
||||
if (directionX == -1) { |
||||
changes.cropperRect.left = cropperRect.left + cropperRect.width - width |
||||
} |
||||
if (directionY == -1) { |
||||
changes.cropperRect.top = cropperRect.top + cropperRect.height - height |
||||
} |
||||
// 边界控制
|
||||
changes.cropperRect.width = width |
||||
changes.cropperRect.height = height |
||||
} |
||||
} else if (touches.length == 2 && startTouchs.length == 2) { |
||||
var changeX2 = touches[0].clientX - touches[1].clientX; |
||||
var changeY2 = touches[0].clientY - touches[1].clientY; |
||||
var distance = Math.pow(changeX2, 2) + Math.pow(changeY2, 2) |
||||
distance = Math.sqrt(distance) |
||||
// 放大比例
|
||||
var scaleRate = distance / startDistance |
||||
this.imageScale(scaleRate) |
||||
} |
||||
}, |
||||
touchEnd(ev) { |
||||
// console.log('end',ev)
|
||||
if(ev.touches.length!==0) return |
||||
if (touchType === "image") { |
||||
var cropperLeft = cropperRect.left |
||||
var cropperRight = cropperRect.left + cropperRect.width |
||||
var cropperTop = cropperRect.top |
||||
var cropperBottom = cropperTop + cropperRect.height |
||||
var rate = changes.imageRect.width / changes.imageRect.height |
||||
var cropperRate = cropperRect.width / cropperRect.height |
||||
if (changes.imageRect.width < cropperRect.width || changes.imageRect.height < cropperRect.height) { |
||||
var scale = 1 |
||||
if (rate < cropperRate) { |
||||
scale = cropperRect.width / changes.imageRect.width |
||||
} else { |
||||
scale = cropperRect.height / changes.imageRect.height |
||||
} |
||||
imageRect.width = changes.imageRect.width |
||||
imageRect.height = changes.imageRect.height |
||||
this.imageScale(scale) |
||||
} |
||||
// 边界控制start
|
||||
if (cropperLeft < changes.imageRect.left) { |
||||
changes.imageRect.left = cropperLeft |
||||
} |
||||
if (cropperRight > changes.imageRect.left + changes.imageRect.width) { |
||||
changes.imageRect.left = cropperRight - changes.imageRect.width |
||||
} |
||||
if (cropperTop < changes.imageRect.top) { |
||||
changes.imageRect.top = cropperTop |
||||
} |
||||
if (cropperBottom > changes.imageRect.top + changes.imageRect.height) { |
||||
changes.imageRect.top = cropperBottom - changes.imageRect.height |
||||
} |
||||
// 边界控制end
|
||||
} |
||||
this.updateData({ |
||||
cropperRect: changes.cropperRect, |
||||
imageRect: changes.imageRect, |
||||
}) |
||||
touchType = "" |
||||
startTouchs = [] |
||||
return false; |
||||
}, |
||||
imageScale(scaleRate) { |
||||
var cw = imageRect.width * (scaleRate - 1) |
||||
var ch = imageRect.height * (scaleRate - 1) |
||||
changes.imageRect = { |
||||
width: imageRect.width + cw, |
||||
height: imageRect.height + ch, |
||||
left: imageRect.left - cw * (touchCenter[0]), |
||||
top: imageRect.top - ch * (touchCenter[1]) |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
export function getTouchPoints(touchs) { |
||||
return Array.from(touchs).map(ev => { |
||||
return [ev.clientX, ev.clientY] |
||||
}) |
||||
} |
||||
// 函数防抖
|
||||
export function debounce(fn, wait = 200) { |
||||
var timer = null; |
||||
return function (){ |
||||
if (timer !== null) { |
||||
clearTimeout(timer); |
||||
} |
||||
timer = setTimeout(fn.bind(this), wait); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @description 睡眠 |
||||
* @param {number} time 等待时间毫秒数 |
||||
*/ |
||||
export function sleep(time = 200) { |
||||
return new Promise(resolve => { |
||||
setTimeout(resolve, time) |
||||
}) |
||||
} |
||||
const systemInfo = uni.getSystemInfoSync(); |
||||
|
||||
export function parseUnit(size){ |
||||
if(typeof size == 'number' || !isNaN(Number(size))){ |
||||
return uni.upx2px(size) |
||||
}else if(typeof size === 'string') { |
||||
if(size.endsWith('rpx')){ |
||||
return parseUnit(size.replace('rpx','')) |
||||
}else if(size.endsWith('px')){ |
||||
return Number(size.replace('px','')) |
||||
}else if(size.endsWith('vw')){ |
||||
return Number(size.replace('vw',''))*systemInfo.screenWidth/100 |
||||
}else if(size.endsWith('vh')){ |
||||
return Number(size.replace('vh',''))*systemInfo.screenHeight/100 |
||||
} |
||||
} |
||||
return 0 |
||||
} |
@ -0,0 +1,81 @@ |
||||
{ |
||||
"id": "bt-cropper", |
||||
"displayName": "bt-cropper图片裁剪插件", |
||||
"version": "3.0.1", |
||||
"description": "一款好用的图片裁剪插件", |
||||
"keywords": [ |
||||
"图片", |
||||
"图片裁剪", |
||||
"图片裁剪", |
||||
"头像裁剪", |
||||
"cropper" |
||||
], |
||||
"repository": "", |
||||
"engines": { |
||||
"HBuilderX": "^3.2.1" |
||||
}, |
||||
"dcloudext": { |
||||
"sale": { |
||||
"regular": { |
||||
"price": "0.00" |
||||
}, |
||||
"sourcecode": { |
||||
"price": "0.00" |
||||
} |
||||
}, |
||||
"contact": { |
||||
"qq": "1097122362" |
||||
}, |
||||
"declaration": { |
||||
"ads": "无", |
||||
"data": "插件不采集任何数据", |
||||
"permissions": "无" |
||||
}, |
||||
"npmurl": "", |
||||
"type": "component-vue" |
||||
}, |
||||
"uni_modules": { |
||||
"dependencies": [], |
||||
"encrypt": [], |
||||
"platforms": { |
||||
"cloud": { |
||||
"tcb": "y", |
||||
"aliyun": "y" |
||||
}, |
||||
"client": { |
||||
"Vue": { |
||||
"vue2": "y", |
||||
"vue3": "y" |
||||
}, |
||||
"App": { |
||||
"app-vue": "y", |
||||
"app-nvue": "n" |
||||
}, |
||||
"H5-mobile": { |
||||
"Safari": "y", |
||||
"Android Browser": "y", |
||||
"微信浏览器(Android)": "y", |
||||
"QQ浏览器(Android)": "y" |
||||
}, |
||||
"H5-pc": { |
||||
"Chrome": "n", |
||||
"IE": "n", |
||||
"Edge": "n", |
||||
"Firefox": "n", |
||||
"Safari": "n" |
||||
}, |
||||
"小程序": { |
||||
"微信": "y", |
||||
"阿里": "u", |
||||
"百度": "u", |
||||
"字节跳动": "y", |
||||
"QQ": "y" |
||||
}, |
||||
"快应用": { |
||||
"华为": "n", |
||||
"联盟": "n" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,109 @@ |
||||
|
||||
|
||||
## bt-cropper 图片裁切 |
||||
> **组件名:bt-cropper** |
||||
|
||||
图片裁切组件,在页面中裁切图片,输出裁切后的图片,支持app,小程序,H5 |
||||
### [在线体验](https://static-a3b890b4-7cb2-4b29-aa78-e652572bdef6.bspapp.com/#/) |
||||
|
||||
> **注意事项** |
||||
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 |
||||
> - 组件需要依赖 `sass` 插件 ,请自行手动安装 |
||||
> - 只测试了头条小程序,app-vue 安卓,微信小程序和H5 大部分平台应该都没问题了 |
||||
> - 包裹层或裁剪器需要手动指定高度和宽度,推荐手动指定裁剪器的大小,尤其是头条小程序,js有时候获取不到容器的大小 |
||||
> - 如使用过程中有任何问题,或者您有一些好的建议,欢迎联系作者微信:1097122362 |
||||
|
||||
|
||||
|
||||
### 安装方式 |
||||
|
||||
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 |
||||
|
||||
### 基本用法 |
||||
|
||||
**示例** |
||||
|
||||
```html |
||||
<template> |
||||
<view class="container"> |
||||
<bt-cropper ref="cropper" :imageSrc="imageSrc"></bt-cropper> |
||||
<button @click="crop">裁切</button> |
||||
</view> |
||||
</template> |
||||
<style> |
||||
.container{ |
||||
/** 外层一定要指定大小 */ |
||||
height:100vh; |
||||
} |
||||
</style> |
||||
``` |
||||
|
||||
|
||||
```javascript |
||||
export default { |
||||
methods:{ |
||||
crop(){ |
||||
// 通过组件定义的ref调用cropper方法,返回一个promise对象 |
||||
this.$refs.cropper.crop().then(([err,res])=>{ |
||||
if(!err){ |
||||
console.log(res) |
||||
}else{ |
||||
console.err(err) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
|
||||
``` |
||||
|
||||
### 限定裁切比例 |
||||
|
||||
bt-cropper,指定ratio即可设置裁切框的宽高比,如果你想让用户自由缩放,将ratio设置为0即可 |
||||
|
||||
**示例** |
||||
|
||||
```html |
||||
<bt-cropper ref="cropper" :ratio="16/9":imageSrc="imageSrc"></bt-cropper> |
||||
``` |
||||
|
||||
## API |
||||
|
||||
### cropper Props |
||||
|
||||
|属性名|类型|默认值|说明| |
||||
|:-:|:-:|:-:|:-:| |
||||
|ratio|number|0|裁切图像的宽高比,0表示自由比例| |
||||
|dWidth|number|0|生成的图片的宽度,单位:px,如果传入0的话就是按原像素的比例裁剪,也就是说,输出图片的清晰度和输入图片的清晰度一样| |
||||
|imageSrc|String|''|原图的路径,支持本地路径和网络路径,如果是网络路径,小程序要注意配置下载域名,H5要注意跨域问题| |
||||
|mask|String|''| 裁剪的蒙版url,配合蒙版可以裁剪出任何形状的图形 (示例见全屏裁剪demo) | |
||||
|fileType|String|'jpg'|目标文件的类型,只支持 'jpg' 或 'png'。默认为 'jpg'| |
||||
|quality|Number|1|图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理| |
||||
|showGrid|Boolean|false|是否显示中心网格线,默认不显示| |
||||
|initPosition|object|null|图片自定义的初始的位置,内容格式见change事件| |
||||
|autoZoom|Boolean|true|是否开启操作结束后自动放大到窗口大小| |
||||
|containerSize|object|null|手动指定容器大小,如果裁剪器放在大小会移动或缩放的dom中,则必须手动指定大小,可以带上单位,如果不带单位默认px,支持的单位有:rpx,px,vw,vm,示例:{width:100,height:1100}或者:{width:'100rpx',height:'100rpx'}| |
||||
|canvas2d|Boolean|false| 是开启新版的canvas | |
||||
|
||||
|
||||
|
||||
### cropper Methods |
||||
|
||||
|方法名称|说明|参数| |
||||
|:-:|:-:|:-:| |
||||
|crop|裁剪图片|开始绘制并开始裁剪图片,返回Promise对象| |
||||
|init|初始化|-| |
||||
|resetImage|重置裁剪框和图片的位置和大小到初始的位置和大小|-| |
||||
|redo|撤销,最多可以回退10步|-| |
||||
|resume|重做|-| |
||||
|
||||
|
||||
### cropper Events |
||||
|
||||
|方法名称|说明|返回值| |
||||
|:-:|:-:|:-:| |
||||
|change|当裁剪框和图片的相对位置发生变化的时候触发,返回裁剪框与图片的相对位置|ev={left:number,top:number,width:number,height:number}| |
||||
|loadFail|当图片加载失败时触发| - | |
||||
|cropStart|当裁开始时触发| - | |
||||
## 帮助 |
||||
在使用中如遇到无法解决的问题,请提 [Issues](https://gitee.com/xiaojiang1996/better-uni-cropper/issues) 或者加我 微信:1097122362。 |
Loading…
Reference in new issue