2024-02-21 17:43:11 +08:00

546 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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;
}
}
})