mirror of
https://gitee.com/elegant_wings/dbd-meeting-wx-small.git
synced 2025-06-21 15:29:37 +08:00
546 lines
16 KiB
JavaScript
546 lines
16 KiB
JavaScript
/**
|
||
* wx-cropper 2.0
|
||
* 基于微信小程序的图片裁剪工具
|
||
* @author ifmiss
|
||
*/
|
||
|
||
// 裁剪图片的宽度设置
|
||
const CROPPER_WIDTH = 720
|
||
|
||
// 裁剪显示的最大比例,如果裁剪的图片过长,则做限制,默认最大宽高比例为 宽640 / 高960 (宽高比例)
|
||
const CROPPER_RATIO = 0.7
|
||
|
||
/**
|
||
* 初始化裁剪的比例 如果是正方形则是 1
|
||
* 比例为宽高比 建议区间为 0.25 - 4
|
||
* 设置为0的时候则是不固定宽高
|
||
*/
|
||
const CROPPER_AREA_RATIO = 1
|
||
|
||
// 裁剪的位置
|
||
let CUT_L, // 初始化拖拽元素的left值
|
||
CUT_T, // 初始化拖拽元素的top值
|
||
CUT_R, // 初始化拖拽元素的
|
||
CUT_B, // 初始化拖拽元素的
|
||
|
||
// 裁剪的宽度
|
||
CUT_W, // 初始化拖拽元素的宽度
|
||
CUT_H, // 初始化拖拽元素的高度
|
||
|
||
// 拖拽相关
|
||
PAGE_X, // 手按下的x位置
|
||
PAGE_Y, // 手按下y的位置
|
||
T_PAGE_X, // 手移动的时候x的位置
|
||
T_PAGE_Y, // 手移动的时候Y的位置x
|
||
|
||
// 图片比例
|
||
IMG_RATIO,
|
||
|
||
// 图片实际宽高
|
||
IMG_REAL_W, // 图片实际的宽度
|
||
IMG_REAL_H, // 图片实际的高度
|
||
|
||
// 裁剪图片区域的信息
|
||
CROPPER_IMG_W,
|
||
CROPPER_IMG_H,
|
||
|
||
// 移动的比例
|
||
DRAFG_MOVE_RATIO = 750 / wx.getSystemInfoSync().windowWidth, //移动时候的比例,
|
||
|
||
|
||
INIT_DRAG_POSITION = 0, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
|
||
|
||
DRAW_IMAGE_W, // 设置生成的图片宽度
|
||
|
||
// 最大可显示得图片宽度,需要设定最大值,否则安卓部分机器会闪退, 控制qualityWidth的最大值
|
||
maxQW = 2550
|
||
|
||
/**
|
||
* 最小裁剪宽度 由于设置了裁剪的UI样式,裁剪的宽高必须要有最小宽度,这个宽度是裁剪长或者宽的最短一方的宽度
|
||
* 如 400 200
|
||
* 那么如果只能设置为100的时候
|
||
* 那么最小缩放到200 100的效果,之后只能放大不能缩小
|
||
*/
|
||
const MIN_CROPPER_DIS = 100
|
||
const app = getApp()
|
||
|
||
|
||
|
||
Page({
|
||
/**
|
||
* 页面的初始数据
|
||
*/
|
||
data: {
|
||
// 之后可以动态替换
|
||
// imageSrc: 'http://www.bing.com/az/hprichbg/rb/BulgariaPerseids_ZH-CN11638911564_1920x1080.jpg',
|
||
// imageSrc: 'http://www.bing.com/az/hprichbg/rb/BulgariaPerseids_ZH-CN11638911564_1920x1080.jpg',
|
||
imageSrc: '/images/vcard.png',
|
||
// 是否显示图片(在图片加载完成之后设置为true)
|
||
isShowImg: false,
|
||
|
||
// 初始化的宽高
|
||
cropperInitW: CROPPER_WIDTH,
|
||
cropperInitH: CROPPER_WIDTH,
|
||
|
||
// 动态的宽高
|
||
cropperW: CROPPER_WIDTH,
|
||
cropperH: CROPPER_WIDTH,
|
||
|
||
// 动态的left top值
|
||
cropperL: 0,
|
||
cropperT: 0,
|
||
|
||
// 图片缩放值
|
||
scaleP: 0,
|
||
|
||
// 裁剪框 宽高
|
||
cutL: 0,
|
||
cutT: 0,
|
||
cutB: 0,
|
||
cutR: 0,
|
||
|
||
qualityWidth: DRAW_IMAGE_W,
|
||
|
||
innerAspectRadio: DRAFG_MOVE_RATIO
|
||
},
|
||
|
||
/**
|
||
* 生命周期函数--监听页面加载
|
||
*/
|
||
onLoad: function (e) {
|
||
this.setData({
|
||
imageSrc: e.img
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 生命周期函数--监听页面初次渲染完成
|
||
*/
|
||
onReady: function () {
|
||
// 初始化
|
||
this.loadImage();
|
||
},
|
||
|
||
/**
|
||
* 选择本地图片
|
||
* 基于底部中间的按钮的点击事件
|
||
*/
|
||
getImage: function () {
|
||
var _this = this
|
||
wx.chooseImage({
|
||
success: function (res) {
|
||
_this.setData({
|
||
isShowImg: false,
|
||
imageSrc: res.tempFilePaths[0],
|
||
})
|
||
_this.loadImage();
|
||
},
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 初始化图片信息
|
||
* 获取图片内容,并初始化裁剪框
|
||
*/
|
||
loadImage: function () {
|
||
var _this = this
|
||
wx.showLoading({
|
||
title: '图片加载中...',
|
||
})
|
||
|
||
wx.getImageInfo({
|
||
src: _this.data.imageSrc,
|
||
success: function success(res) {
|
||
DRAW_IMAGE_W = IMG_REAL_W = res.width
|
||
IMG_REAL_H = res.height
|
||
IMG_RATIO = IMG_REAL_W / IMG_REAL_H
|
||
|
||
let MIN_RANGE = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
|
||
|
||
// 用于设置图片的比例(以设置裁剪的比例,方便定位裁剪的left right top bottom)
|
||
INIT_DRAG_POSITION = MIN_RANGE > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : MIN_RANGE
|
||
|
||
// 拿到裁剪位置
|
||
let cropperPosition = _this.initCropperPosition(IMG_RATIO, CROPPER_WIDTH)
|
||
|
||
// 根据图片的宽高显示不同的效果 保证图片可以正常显示
|
||
if (IMG_RATIO >= 1) {
|
||
_this.setData({
|
||
cropperW: CROPPER_WIDTH,
|
||
cropperH: CROPPER_WIDTH / IMG_RATIO,
|
||
// 初始化left right
|
||
cropperL: Math.ceil((CROPPER_WIDTH - CROPPER_WIDTH) / 2),
|
||
cropperT: 0,
|
||
cutL: cropperPosition.left,
|
||
cutT: cropperPosition.top,
|
||
cutR: cropperPosition.right,
|
||
cutB: cropperPosition.bottom,
|
||
// 图片缩放值
|
||
scaleP: IMG_REAL_W / CROPPER_WIDTH,
|
||
qualityWidth: DRAW_IMAGE_W > maxQW ? maxQW : DRAW_IMAGE_W,
|
||
innerAspectRadio: IMG_RATIO
|
||
})
|
||
} else {
|
||
// 此时需要判断图片的比例以设定显示裁剪区域的比例
|
||
// let cropper_real_ratio = CROPPER_RATIO > IMG_RATIO ? CROPPER_RATIO : IMG_RATIO
|
||
|
||
if (CROPPER_RATIO > IMG_RATIO) {
|
||
CROPPER_IMG_W = CROPPER_WIDTH / CROPPER_RATIO * IMG_RATIO
|
||
CROPPER_IMG_H = CROPPER_WIDTH / CROPPER_RATIO
|
||
} else {
|
||
CROPPER_IMG_W = CROPPER_WIDTH
|
||
CROPPER_IMG_H = CROPPER_IMG_W / IMG_RATIO
|
||
}
|
||
|
||
// 动态生成新的CROPPER的真实宽度 高度
|
||
// CROPPER_IMG_W = CROPPER_WIDTH * cropper_real_ratio
|
||
// CROPPER_IMG_H = CROPPER_WIDTH / cropper_real_ratio / IMG_RATIO
|
||
// console.log(cropper_real_ratio)
|
||
// console.log(CROPPER_IMG_W)
|
||
// console.log(CROPPER_IMG_H)
|
||
// console.log(CROPPER_IMG_W / CROPPER_IMG_H)
|
||
|
||
_this.setData({
|
||
cropperW: CROPPER_IMG_W,
|
||
cropperH: CROPPER_IMG_H,
|
||
// 初始化left right
|
||
cropperL: Math.ceil((CROPPER_WIDTH - CROPPER_IMG_W) / 2),
|
||
cropperT: 0,
|
||
|
||
cutL: cropperPosition.left,
|
||
cutT: cropperPosition.top,
|
||
cutR: cropperPosition.right,
|
||
cutB: cropperPosition.bottom,
|
||
// 图片缩放值
|
||
scaleP: IMG_REAL_W / CROPPER_IMG_W,
|
||
qualityWidth: DRAW_IMAGE_W > maxQW ? maxQW : DRAW_IMAGE_W,
|
||
innerAspectRadio: IMG_RATIO
|
||
})
|
||
}
|
||
_this.setData({
|
||
isShowImg: true
|
||
})
|
||
wx.hideLoading()
|
||
}
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 初始化裁剪区域的
|
||
* left right top bottom
|
||
* 需要 CROPPER_AREA_RATIO 来判断
|
||
* @return 返回裁剪的left, right, top bottom的值
|
||
*/
|
||
initCropperPosition(radio) {
|
||
let left = 0,
|
||
right = 0,
|
||
top = 0,
|
||
bottom = 0,
|
||
cropperW,
|
||
cropperH
|
||
// 如果 CROPPER_AREA_RATIO = 0 则不限制固定宽高
|
||
if (CROPPER_AREA_RATIO === 0) return {
|
||
left,
|
||
right,
|
||
top,
|
||
bottom
|
||
}
|
||
|
||
// 宽大于等于高
|
||
if (radio >= 1) {
|
||
cropperW = CROPPER_WIDTH
|
||
cropperH = CROPPER_WIDTH / IMG_RATIO
|
||
if (radio > CROPPER_AREA_RATIO) {
|
||
return {
|
||
left: Math.ceil((cropperW - cropperH * CROPPER_AREA_RATIO) / 2),
|
||
right: Math.ceil((cropperW - cropperH * CROPPER_AREA_RATIO) / 2),
|
||
top: 0,
|
||
bottom: 0
|
||
}
|
||
}
|
||
return {
|
||
left: 0,
|
||
right: 0,
|
||
top: Math.ceil((cropperH - cropperW / CROPPER_AREA_RATIO) / 2),
|
||
bottom: Math.ceil((cropperH - cropperW / CROPPER_AREA_RATIO) / 2)
|
||
}
|
||
}
|
||
|
||
// 此时需要判断图片的比例以设定显示裁剪区域的比例
|
||
let cropper_real_ratio = CROPPER_RATIO > IMG_RATIO ? CROPPER_RATIO : IMG_RATIO
|
||
// 高大于宽
|
||
cropperW = CROPPER_WIDTH / cropper_real_ratio * IMG_RATIO
|
||
cropperH = CROPPER_WIDTH / cropper_real_ratio
|
||
if (radio < CROPPER_AREA_RATIO) {
|
||
return {
|
||
left: 0,
|
||
right: 0,
|
||
top: Math.ceil((cropperH - cropperW / CROPPER_AREA_RATIO) / 2),
|
||
bottom: Math.ceil((cropperH - cropperW / CROPPER_AREA_RATIO) / 2)
|
||
}
|
||
}
|
||
return {
|
||
left: Math.ceil((cropperW - cropperH * CROPPER_AREA_RATIO) / 2),
|
||
right: Math.ceil((cropperW - cropperH * CROPPER_AREA_RATIO) / 2),
|
||
top: 0,
|
||
bottom: 0
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 拖动时候触发的touchStart事件
|
||
*/
|
||
contentStartMove(e) {
|
||
PAGE_X = e.touches[0].pageX
|
||
PAGE_Y = e.touches[0].pageY
|
||
},
|
||
|
||
/**
|
||
* 拖动时候触发的touchMove事件
|
||
*/
|
||
contentMoveing(e) {
|
||
var _this = this
|
||
var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||
var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO * DRAFG_MOVE_RATIO
|
||
|
||
// 左移右移
|
||
if (dragLengthX > 0) {
|
||
if (this.data.cutL - dragLengthX < 0) dragLengthX = this.data.cutL
|
||
} else {
|
||
if (this.data.cutR + dragLengthX < 0) dragLengthX = -this.data.cutR
|
||
}
|
||
|
||
|
||
// 上移下移
|
||
if (dragLengthY > 0) {
|
||
if (this.data.cutT - dragLengthY < 0) dragLengthY = this.data.cutT
|
||
} else {
|
||
if (this.data.cutB + dragLengthY < 0) dragLengthY = -this.data.cutB
|
||
}
|
||
this.setData({
|
||
cutL: this.data.cutL - dragLengthX,
|
||
cutT: this.data.cutT - dragLengthY,
|
||
cutR: this.data.cutR + dragLengthX,
|
||
cutB: this.data.cutB + dragLengthY
|
||
})
|
||
|
||
|
||
PAGE_X = e.touches[0].pageX
|
||
PAGE_Y = e.touches[0].pageY
|
||
},
|
||
|
||
contentTouchEnd() {
|
||
|
||
},
|
||
|
||
/**
|
||
* 点击取消关闭裁剪页面
|
||
*/
|
||
close() {
|
||
// wx.redirectTo()
|
||
},
|
||
|
||
/**
|
||
* 点击完成之后
|
||
* 生成图片信息
|
||
*/
|
||
getImageInfo() {
|
||
var _this = this
|
||
wx.showLoading({
|
||
title: '图片生成中...',
|
||
})
|
||
// 将图片写入画布
|
||
const ctx = wx.createCanvasContext('myCanvas')
|
||
let w = this.data.qualityWidth
|
||
let h = this.data.qualityWidth / IMG_RATIO
|
||
ctx.drawImage(_this.data.imageSrc, 0, 0, w, h);
|
||
ctx.draw(true, () => {
|
||
// 获取画布要裁剪的位置和宽度 均为百分比 * 画布中图片的宽度 保证了在微信小程序中裁剪的图片模糊 位置不对的问题
|
||
var canvasW = ((_this.data.cropperW - _this.data.cutL - _this.data.cutR) / _this.data.cropperW) * w
|
||
var canvasH = ((_this.data.cropperH - _this.data.cutT - _this.data.cutB) / _this.data.cropperH) * h
|
||
var canvasL = (_this.data.cutL / _this.data.cropperW) * w
|
||
var canvasT = (_this.data.cutT / _this.data.cropperH) * h
|
||
|
||
// 生成图片
|
||
wx.canvasToTempFilePath({
|
||
x: canvasL,
|
||
y: canvasT,
|
||
width: canvasW,
|
||
height: canvasH,
|
||
destWidth: canvasW,
|
||
destHeight: canvasH,
|
||
quality: 0.5,
|
||
canvasId: 'myCanvas',
|
||
success: function (res) {
|
||
wx.hideLoading()
|
||
app.Upload(res.tempFilePath, function (res) {
|
||
if (res.code == 0) {
|
||
app.AjaxRequest('POST', {
|
||
'content-type': 'application/json',
|
||
'Authorization': 'Bearer ' + app.Getopenid()
|
||
}, '/user/profile/update', {
|
||
avatar: res.fileName
|
||
}, function (ret) {
|
||
wx.navigateBack()
|
||
})
|
||
}
|
||
})
|
||
}
|
||
})
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 设置大小的时候触发的touchStart事件
|
||
* 存数据
|
||
*/
|
||
dragStart(e) {
|
||
T_PAGE_X = e.touches[0].pageX
|
||
T_PAGE_Y = e.touches[0].pageY
|
||
CUT_L = this.data.cutL
|
||
CUT_R = this.data.cutR
|
||
CUT_B = this.data.cutB
|
||
CUT_T = this.data.cutT
|
||
},
|
||
|
||
/**
|
||
* 设置大小的时候触发的touchMove事件
|
||
* 根据dragType判断类型
|
||
* 4个方向的边线拖拽效果
|
||
* 右下角按钮的拖拽效果
|
||
*/
|
||
dragMove(e) {
|
||
var _this = this
|
||
var dragType = e.target.dataset.drag
|
||
switch (dragType) {
|
||
case 'right':
|
||
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||
if (CUT_R + dragLength < 0) dragLength = -CUT_R
|
||
if (CUT_R + dragLength + MIN_CROPPER_DIS > this.data.cropperW - this.data.cutL) dragLength = (this.data.cropperW - this.data.cutL) - MIN_CROPPER_DIS - CUT_R
|
||
|
||
if (CROPPER_AREA_RATIO) {
|
||
// 底部线的限制 不允许超出
|
||
// dragLength 最大不能超过CUT_B
|
||
if (CUT_B + dragLength / CROPPER_AREA_RATIO <= 0) {
|
||
this.setData({
|
||
cutB: 0
|
||
})
|
||
return
|
||
}
|
||
this.setData({
|
||
cutR: CUT_R + dragLength,
|
||
cutB: CUT_B + dragLength / CROPPER_AREA_RATIO
|
||
})
|
||
} else {
|
||
this.setData({
|
||
cutR: CUT_R + dragLength
|
||
})
|
||
}
|
||
|
||
break;
|
||
case 'left':
|
||
var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||
if (CUT_L - dragLength < 0) dragLength = CUT_L
|
||
if ((CUT_L - dragLength + MIN_CROPPER_DIS) > (this.data.cropperW - this.data.cutR)) dragLength = CUT_L - (this.data.cropperW - this.data.cutR) + MIN_CROPPER_DIS
|
||
|
||
if (CROPPER_AREA_RATIO) {
|
||
// 顶部线的限制 不允许超出
|
||
// dragLength 最大不能超过CUT_T
|
||
if (CUT_T - dragLength / CROPPER_AREA_RATIO < 0) {
|
||
this.setData({
|
||
cutT: 0
|
||
})
|
||
return
|
||
}
|
||
this.setData({
|
||
cutL: CUT_L - dragLength,
|
||
cutT: CUT_T - dragLength / CROPPER_AREA_RATIO
|
||
})
|
||
} else {
|
||
this.setData({
|
||
cutL: CUT_L - dragLength
|
||
})
|
||
}
|
||
break;
|
||
case 'top':
|
||
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||
if (CUT_T - dragLength < 0) dragLength = CUT_T
|
||
if ((CUT_T - dragLength + MIN_CROPPER_DIS) > this.data.cropperH - this.data.cutB) dragLength = CUT_T - (this.data.cropperH - this.data.cutB) + MIN_CROPPER_DIS
|
||
|
||
if (CROPPER_AREA_RATIO) {
|
||
// left 线的限制 不允许超出
|
||
// dragLength 最大不能超过CUT_L
|
||
if (CUT_L - dragLength * CROPPER_AREA_RATIO < 0) {
|
||
this.setData({
|
||
cutL: 0
|
||
})
|
||
return
|
||
}
|
||
this.setData({
|
||
cutL: CUT_L - dragLength * CROPPER_AREA_RATIO,
|
||
cutT: CUT_T - dragLength
|
||
})
|
||
} else {
|
||
this.setData({
|
||
cutT: CUT_T - dragLength
|
||
})
|
||
}
|
||
break;
|
||
case 'bottom':
|
||
var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||
if (CUT_B + dragLength < 0) dragLength = -CUT_B
|
||
if (CUT_B + dragLength + MIN_CROPPER_DIS > this.data.cropperH - this.data.cutT) dragLength = (this.data.cropperH - this.data.cutT) - MIN_CROPPER_DIS - CUT_B
|
||
|
||
if (CROPPER_AREA_RATIO) {
|
||
// right 线的限制 不允许超出
|
||
// dragLength 最大不能超过 CUT_R
|
||
if (CUT_R + dragLength * CROPPER_AREA_RATIO < 0) {
|
||
this.setData({
|
||
cutR: 0
|
||
})
|
||
return
|
||
}
|
||
this.setData({
|
||
cutR: CUT_R + dragLength * CROPPER_AREA_RATIO,
|
||
cutB: CUT_B + dragLength
|
||
})
|
||
} else {
|
||
this.setData({
|
||
cutB: CUT_B + dragLength
|
||
})
|
||
}
|
||
break;
|
||
case 'rightBottom':
|
||
var dragType = e.target.dataset.drag
|
||
var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
|
||
var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
|
||
if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
|
||
if (CUT_B + dragLengthY + MIN_CROPPER_DIS > this.data.cropperH - this.data.cutT) dragLengthY = (this.data.cropperH - this.data.cutT) - MIN_CROPPER_DIS - CUT_B
|
||
if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
|
||
if (CUT_R + dragLengthX + MIN_CROPPER_DIS > this.data.cropperW - this.data.cutL) dragLengthX = (this.data.cropperW - this.data.cutL) - MIN_CROPPER_DIS - CUT_R
|
||
|
||
if (CROPPER_AREA_RATIO) {
|
||
// right 线的限制 不允许超出
|
||
// dragLength 最大不能超过 CUT_R
|
||
if (CUT_R + dragLengthY * CROPPER_AREA_RATIO < 0) {
|
||
this.setData({
|
||
cutR: 0
|
||
})
|
||
return
|
||
}
|
||
this.setData({
|
||
cutR: CUT_R + dragLengthY * CROPPER_AREA_RATIO,
|
||
cutB: CUT_B + dragLengthY
|
||
})
|
||
} else {
|
||
this.setData({
|
||
cutB: CUT_B + dragLengthY,
|
||
cutR: CUT_R + dragLengthX
|
||
})
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}) |