ufutx_love_mp/src/components/wepy-image-cropper.wpy

1293 lines
42 KiB
Plaintext
Raw Normal View History

2024-09-26 11:41:20 +08:00
<template>
<view class="image-cropper" catchtouchmove="_preventTouchMove">
<view class="main" id="main_bc" @touchend="_cutTouchEnd" @touchstart="_cutTouchStart" @touchmove="_cutTouchMove">
<view class="content">
<view class="content_top bg_gray {{_flag_bright? '':'bg_black'}}"
style="height:{{cut_top}}px;transition-property:{{_cut_animation?'':'background'}}"></view>
<view class="content_middle" style="height:{{height}}px;">
<view class="content_middle_left bg_gray {{_flag_bright? '':'bg_black'}}"
style="width:{{cut_left}}px;transition-property:{{_cut_animation?'':'background'}}"></view>
<view class="content_middle_middle"
style="width:{{width}}px;height:{{height}}px;transition-duration: .3s;transition-property:{{_cut_animation?'':'background'}};">
<view class="border border-top-left" style="background: {{borderColor}}"></view>
<view class="border border-top-right" style="background: {{borderColor}}"></view>
<view class="border border-right-top" style="background: {{borderColor}}"></view>
<view class="border border-right-bottom" style="background: {{borderColor}}"></view>
<view class="border border-bottom-right" style="background: {{borderColor}}"></view>
<view class="border border-bottom-left" style="background: {{borderColor}}"></view>
<view class="border border-left-bottom" style="background: {{borderColor}}"></view>
<view class="border border-left-top" style="background: {{borderColor}}"></view>
</view>
<view class="content_middle_right bg_gray {{_flag_bright? '':'bg_black'}}"
style="transition-property:{{_cut_animation?'':'background'}}"></view>
</view>
<view class="content_bottom bg_gray {{_flag_bright?'':'bg_black'}}"
style="transition-property:{{_cut_animation?'':'background'}}"></view>
</view>
<image @load="imageLoad" bindtouchstart="_start" bindtouchmove="_move" bindtouchend="_end"
style="width:{{img_width ? img_width + 'px' : 'auto'}};height:{{img_height ? img_height + 'px' : 'auto'}};top:{{tanTop}};left:{{tanLeft}};transform:translate3d({{tanTop=='50%'?'-'+tanTop:_img_left-img_width/2+'px'}},{{tanLeft=='50%'?'-'+tanLeft:_img_top-img_height/2+'px'}},0) scale({{scale}}) rotate({{angle}}deg);transition-duration:{{_cut_animation?.4:0}}s;"
class="img"
:src="imgSrc"></image>
</view>
<canvas canvas-id="image-cropper" disable-scroll="true"
style="width:{{_canvas_width * export_scale}}px;height:{{_canvas_height * export_scale}}px;left:{{canvas_left}}px;top:{{canvas_top}}px"
class="image-cropper-canvas"></canvas>
</view>
<view class="main-box">
<view class="main-cancel ~flo_l ~font_30 ~text-center" @tap.stop="chooseimage">选择图片</view>
<view class="main-save ~flo_r ~font_30 ~text-center" @tap.stop="saveUpdate">确定</view>
</view>
<chooseImageTips :chooseShow.sync="chooseShow" @hideCut.user="hideCut" @imgSrcCut.user="imgSrcCut"></chooseImageTips>
</template>
<script>
import wepy from '@wepy/core'
wepy.component({
// config = {
// component: true
// }
// components = {chooseImageTips}
props: {
/**
* 图片路径
*/
imgSrc: {
type: String
},
/**
* 裁剪框颜色
*/
borderColor: {
type: String,
default: 'white'
},
/**
* 裁剪框高度
*/
height: {
type: Number,
default: 200
},
/**
* 裁剪框宽度
*/
width: {
type: Number,
default: 200
},
/**
* 裁剪框最小尺寸
*/
min_width: {
type: Number,
default: 100
},
min_height: {
type: Number,
default: 100
},
/**
* 裁剪框最大尺寸
*/
max_width: {
type: Number,
default: 300
},
'max_height': {
type: Number,
default: 300
},
/**
* 裁剪框禁止拖动
*/
disable_width: {
type: Boolean,
default: false
},
disable_height: {
type: Boolean,
default: false
},
/**
* 锁定裁剪框比例
*/
disable_ratio: {
type: Boolean,
default: false
},
/**
* 生成的图片尺寸相对剪裁框的比例
*/
export_scale: {
type: Number,
default: 1
},
/**
* 生成的图片质量0-1
*/
quality: {
type: Number,
default: 0
},
cut_top: {
type: Number,
default: 212
},
cut_left: {
type: Number,
default: 37
},
/**
* canvas上边距不设置默认不显示
*/
canvas_top: {
type: Number,
default: null
},
/**
* canvas左边距不设置默认不显示
*/
canvas_left: {
type: Number,
default: null
},
/**
* 图片宽度
*/
img_width: {
type: Number,
default: null
},
/**
* 图片高度
*/
img_height: {
type: Number,
default: null
},
/**
* 图片缩放比
*/
scale: {
type: Number,
default: 1
},
/**
* 图片旋转角度
*/
angle: {
type: Number,
default: 0
},
/**
* 判断是否是新图片
*/
newPic: {
type: Number,
default: 0
},
/**
* 最小缩放比
*/
min_scale: {
type: Number,
default: 0.5
},
/**
* 最大缩放比
*/
max_scale: {
type: Number,
default: 2
},
/**
* 是否禁用旋转
*/
disable_rotate: {
type: Boolean,
default: false
},
/**
* 是否限制移动范围(剪裁框只能在图片内)
*/
limit_move: {
type: Boolean,
default: false
}
},
data: {
el: 'image-cropper', // 暂时无用
info: wx.getSystemInfoSync(),
MOVE_THROTTLE: null, // 触摸移动节流settimeout
MOVE_THROTTLE_FLAG: true, // 节流标识
INIT_IMGWIDTH: 0, // 图片设置尺寸,此值不变(记录最初设定的尺寸)
INIT_IMGHEIGHT: 0, // 图片设置尺寸,此值不变(记录最初设定的尺寸)
TIME_BG: null, // 背景变暗延时函数
TIME_CUT_CENTER: null,
_touch_img_relative: [{
x: 0,
y: 0
}], // 鼠标和图片中心的相对位置
_flag_cut_touch: false, // 是否是拖动裁剪框
_hypotenuse_length: 0, // 双指触摸时斜边长度
_flag_img_endtouch: false, // 是否结束触摸
_flag_bright: true, // 背景是否亮
_canvas_overflow: true, // canvas缩略图是否在屏幕外面
_canvas_width: 200,
_canvas_height: 200,
origin_x: 0.5, // 图片旋转中心
origin_y: 0.5, // 图片旋转中心
_cut_animation: false, // 是否开启图片和裁剪框过渡
_img_top: wx.getSystemInfoSync().windowHeight / 2, // 图片上边距
_img_left: wx.getSystemInfoSync().windowWidth / 2, // 图片左边距
chooseShow: false,
tanTop: '50%',
tanLeft: '50%'
},
watch: {
// 监听截取框宽高变化
width(newV, oldV) {
if (newV < this.min_width) {
this.width = this.min_width
}
this._computeCutSize()
},
height(newV, oldV) {
if (newV < this.min_height) {
this.height = this.min_height
}
this._computeCutSize()
},
angle(newV, oldV) {
// 停止居中裁剪框,继续修改图片位置
this._moveStop()
if (this.limit_move) {
if (this.angle % 90) {
this.angle = Math.round(this.angle / 90) * 90
}
}
},
_cut_animation(newV, oldV) {
// 开启过渡300毫秒之后自动关闭
clearTimeout(this._cut_animation_time)
if (newV) {
this._cut_animation_time = setTimeout(() => {
this._cut_animation = false
}, 300)
}
},
limit_move(newV) {
if (newV) {
if (this.angle % 90) {
this.angle = Math.round(this.angle / 90) * 90
}
this._imgMarginDetectionScale()
!this._canvas_overflow && this._draw()
}
},
canvas_top() {
this._canvasDetectionPosition()
},
canvas_left() {
this._canvasDetectionPosition()
},
imgSrc(e) {
this.pushImg()
},
cut_top() {
this._cutDetectionPosition()
if (this.limit_move) {
!this._canvas_overflow && this._draw()
}
},
cut_left() {
this._cutDetectionPosition()
if (this.limit_move) {
!this._canvas_overflow && this._draw()
}
}
},
ready() {
this.info = wx.getSystemInfoSync()
this.INIT_IMGWIDTH = this.img_width
this.INIT_IMGHEIGHT = this.img_height
this._canvas_height = this.height
this._canvas_width = this.width
console.log('--9988')
this._initCanvas()
this.imgSrc && (this.imgSrc = this.imgSrc)
// 根据开发者设置的图片目标尺寸计算实际尺寸
this._initImageSize()
// 设置裁剪框大小>设置图片尺寸>绘制canvas
this._computeCutSize()
// 检查裁剪框是否在范围内
this._cutDetectionPosition()
// 检查canvas是否在范围内
this._canvasDetectionPosition()
this.setCutCenter()
this.$emit('load', {
cropper: this
})
},
// 渲染
methods: {
_draw(callback) {
console.log(this.imgSrc)
if (!this.imgSrc) return
let draw = () => {
// 图片实际大小
let img_width = this.img_width * this.scale * this.export_scale
let img_height = this.img_height * this.scale * this.export_scale
// canvas和图片的相对距离
var xpos = this._img_left - this.cut_left
var ypos = this._img_top - this.cut_top
// 旋转画布
this.ctx.translate(xpos * this.export_scale, ypos * this.export_scale)
this.ctx.rotate(this.angle * Math.PI / 180)
this.ctx.drawImage(this.imgSrc, -img_width / 2, -img_height / 2, img_width, img_height)
this.ctx.draw(false, () => {
callback && callback()
})
}
console.log(this.ctx, 'this.ctx=333')
if (this.ctx.width != this.width || this.ctx.height != this.height) {
// 优化拖动裁剪框,所以必须把宽高设置放在离用户触发渲染最近的地方
this._canvas_height = this.height
this._canvas_width = this.width
draw()
this._canvas_height = this.height
this._canvas_width = this.width
} else {
draw()
}
},
chooseimage() {
let vm = this
vm.chooseShow = true
vm.newPic = 1
// wx.chooseImage({
// count: 1,
// // sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
// sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
// sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
// success: (res) => {
// vm.imgSrc = res.tempFilePaths[0]
// console.log(vm.imgSrc)
// }
// })
},
async saveUpdate(event) {
// 生成图片并回调
await this._initCanvas()
let vm = this
console.log(this.imgSrc, '//')
if (!this.imgSrc) return
if (vm.newPic == 0) {
console.log('222')
vm.$emit('saveCut', {
url: this.imgSrc
})
return
}
let draw = () => {
// 图片实际大小
let img_width = this.img_width * this.scale * this.export_scale
let img_height = this.img_height * this.scale * this.export_scale
// canvas和图片的相对距离
var xpos = this._img_left - this.cut_left
var ypos = this._img_top - this.cut_top
// 旋转画布
vm.ctx.translate(xpos * vm.export_scale, ypos * vm.export_scale)
vm.ctx.rotate(this.angle * Math.PI / 180)
vm.ctx.drawImage(vm.imgSrc, -img_width / 2, -img_height / 2, img_width, img_height)
console.log(vm.ctx, '333')
vm.ctx.draw(false, () => {
console.log('444')
wx.canvasToTempFilePath({
width: vm.width * vm.export_scale,
height: Math.round(vm.height * vm.export_scale),
destWidth: vm.width * vm.export_scale,
destHeight: Math.round(vm.height) * vm.export_scale,
fileType: 'png',
quality: vm.quality,
canvasId: vm.el,
success: (res) => {
console.log(res.tempFilePath, '///')
vm.$emit('saveCut', {
url: res.tempFilePath,
width: vm.width * vm.export_scale,
height: vm.height * vm.export_scale
})
}
}, vm.$wx)
})
}
if (vm.ctx.width != vm.width || vm.ctx.height != vm.height) {
// 优化拖动裁剪框,所以必须把宽高设置放在离用户触发渲染最近的地方
vm._canvas_height = vm.height
vm._canvas_width = vm.width
draw()
vm._canvas_height = vm.height
vm._canvas_width = vm.width
} else {
draw()
}
},
_preventTouchMove(event) {
},
// 裁剪框处理
_cutTouchMove(e) {
if (this._flag_cut_touch && this.MOVE_THROTTLE_FLAG) {
// if (this.disable_ratio && (this.disable_width || this.disable_height)) return
// 节流
this.MOVE_THROTTLE_FLAG = false
this._move_throttle()
let width = this.width,
height = this.height,
cut_top = this.cut_top,
cut_left = this.cut_left,
size_correct = () => {
width = width <= this.max_width ? width >= this.min_width ? width : this.min_width : this.max_width
height = height <= this.max_height ? height >= this.min_height ? height : this.min_height : this.max_height
},
size_inspect = () => {
if ((width > this.max_width || width < this.min_width || height > this.max_height || height < this.min_height) && this.disable_ratio) {
size_correct()
return false
} else {
size_correct()
return true
}
}
height = this.CUT_START.height + ((this.CUT_START.corner > 1 && this.CUT_START.corner < 4 ? 1 : -1) * (this.CUT_START.y - e.touches[0].clientY))
switch (this.CUT_START.corner) {
case 1:
width = this.CUT_START.width + this.CUT_START.x - e.touches[0].clientX
if (this.disable_ratio) {
height = width / (this.width / this.height)
}
if (!size_inspect()) return
cut_left = this.CUT_START.cut_left - (width - this.CUT_START.width)
break
case 2:
width = this.CUT_START.width + this.CUT_START.x - e.touches[0].clientX
if (this.disable_ratio) {
height = width / (this.width / this.height)
}
if (!size_inspect()) return
cut_top = this.CUT_START.cut_top - (height - this.CUT_START.height)
cut_left = this.CUT_START.cut_left - (width - this.CUT_START.width)
break
case 3:
width = this.CUT_START.width - this.CUT_START.x + e.touches[0].clientX
if (this.disable_ratio) {
height = width / (this.width / this.height)
}
if (!size_inspect()) return
cut_top = this.CUT_START.cut_top - (height - this.CUT_START.height)
break
case 4:
width = this.CUT_START.width - this.CUT_START.x + e.touches[0].clientX
if (this.disable_ratio) {
height = width / (this.width / this.height)
}
if (!size_inspect()) return
break
}
if (!this.disable_width && !this.disable_height) {
this.width = width
this.cut_left = cut_left
this.height = height
this.cut_top = cut_top
} else if (!this.disable_width) {
this.width = width
this.cut_left = cut_left
} else if (!this.disable_height) {
this.height = height
this.cut_top = cut_top
}
this._imgMarginDetectionScale()
}
},
_cutTouchStart(e) {
let currentX = e.touches[0].clientX
let currentY = e.touches[0].clientY
let cutbox_top4 = this.cut_top + this.height - 30
let cutbox_bottom4 = this.cut_top + this.height + 20
let cutbox_left4 = this.cut_left + this.width - 30
let cutbox_right4 = this.cut_left + this.width + 30
let cutbox_top3 = this.cut_top - 30
let cutbox_bottom3 = this.cut_top + 30
let cutbox_left3 = this.cut_left + this.width - 30
let cutbox_right3 = this.cut_left + this.width + 30
let cutbox_top2 = this.cut_top - 30
let cutbox_bottom2 = this.cut_top + 30
let cutbox_left2 = this.cut_left - 30
let cutbox_right2 = this.cut_left + 30
let cutbox_top1 = this.cut_top + this.height - 30
let cutbox_bottom1 = this.cut_top + this.height + 30
let cutbox_left1 = this.cut_left - 30
let cutbox_right1 = this.cut_left + 30
if (currentX > cutbox_left4 && currentX < cutbox_right4 && currentY > cutbox_top4 && currentY < cutbox_bottom4) {
this._moveDuring()
this._flag_cut_touch = true
this._flag_img_endtouch = true
this.CUT_START = {
width: this.width,
height: this.height,
x: currentX,
y: currentY,
corner: 4
}
} else if (currentX > cutbox_left3 && currentX < cutbox_right3 && currentY > cutbox_top3 && currentY < cutbox_bottom3) {
this._moveDuring()
this._flag_cut_touch = true
this._flag_img_endtouch = true
this.CUT_START = {
width: this.width,
height: this.height,
x: currentX,
y: currentY,
cut_top: this.cut_top,
cut_left: this.cut_left,
corner: 3
}
} else if (currentX > cutbox_left2 && currentX < cutbox_right2 && currentY > cutbox_top2 && currentY < cutbox_bottom2) {
this._moveDuring()
this._flag_cut_touch = true
this._flag_img_endtouch = true
this.CUT_START = {
width: this.width,
height: this.height,
cut_top: this.cut_top,
cut_left: this.cut_left,
x: currentX,
y: currentY,
corner: 2
}
} else if (currentX > cutbox_left1 && currentX < cutbox_right1 && currentY > cutbox_top1 && currentY < cutbox_bottom1) {
this._moveDuring()
this._flag_cut_touch = true
this._flag_img_endtouch = true
this.CUT_START = {
width: this.width,
height: this.height,
cut_top: this.cut_top,
cut_left: this.cut_left,
x: currentX,
y: currentY,
corner: 1
}
}
},
_cutTouchEnd(e) {
this._moveStop()
this._flag_cut_touch = false
// console.log(e,'_cutTouchEnd;;;')
},
// 点击中间剪裁框处理
_click(event) {
console.log(event)
if (!this.imgSrc) {
// 调起上传
this.chooseimage()
return
}
this._draw(() => {
let x = event.detail ? event.detail.x : event.touches[0].clientX
let y = event.detail ? event.detail.y : event.touches[0].clientY
if ((x >= this.cut_left && x <= (this.cut_left + this.width)) && (y >= this.cut_top && y <= (this.cut_top + this.height))) {
// 生成图片并回调
wx.canvasToTempFilePath({
width: this.width * this.export_scale,
height: Math.round(this.height * this.export_scale),
destWidth: this.width * this.export_scale,
destHeight: Math.round(this.height) * this.export_scale,
fileType: 'png',
quality: this.quality,
canvasId: this.el,
success: (res) => {
this.$emit('tapcut', {
url: res.tempFilePath,
width: this.width * this.export_scale,
height: this.height * this.export_scale
})
}
}, this.$wx)
}
})
},
imageLoad(e) {
this.$emit('imageload', this.imageObject)
wx.hideLoading() // 重置图片角度、缩放、位置
this.imgReset()
setTimeout(() => {
}, 100)
},
// 开始触摸
_start(event) {
this._flag_img_endtouch = false
if (event.touches.length == 1) {
// 单指拖动
this._touch_img_relative[0] = {
x: (event.touches[0].clientX - this._img_left),
y: (event.touches[0].clientY - this._img_top)
}
} else {
// 双指放大
let width = Math.abs(event.touches[0].clientX - event.touches[1].clientX)
let height = Math.abs(event.touches[0].clientY - event.touches[1].clientY)
this._touch_img_relative = [{
x: (event.touches[0].clientX - this._img_left),
y: (event.touches[0].clientY - this._img_top)
}, {
x: (event.touches[1].clientX - this._img_left),
y: (event.touches[1].clientY - this._img_top)
}]
this._hypotenuse_length = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
}
!this._canvas_overflow && this._draw()
},
_move(event) {
// console.log('asssssss',event)
if (this._flag_img_endtouch || !this.MOVE_THROTTLE_FLAG) return
this.MOVE_THROTTLE_FLAG = false
this._move_throttle()
this._moveDuring()
if (event.touches.length == 1) {
// 单指拖动
let left = (event.touches[0].clientX - this._touch_img_relative[0].x),
top = (event.touches[0].clientY - this._touch_img_relative[0].y)
// 图像边缘检测,防止截取到空白
this._img_left = left
this._img_top = top
this._imgMarginDetectionPosition()
this._img_left = this._img_left
this._img_top = this._img_top
} else {
// 双指放大
let width = (Math.abs(event.touches[0].clientX - event.touches[1].clientX)),
height = (Math.abs(event.touches[0].clientY - event.touches[1].clientY)),
hypotenuse = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)),
scale = this.scale * (hypotenuse / this._hypotenuse_length),
current_deg = 0
scale = scale <= this.min_scale ? this.min_scale : scale
scale = scale >= this.max_scale ? this.max_scale : scale
// 图像边缘检测,防止截取到空白
this.scale = scale
this._imgMarginDetectionScale()
// 双指旋转(如果没禁用旋转)
let _touch_img_relative = [{
x: (event.touches[0].clientX - this._img_left),
y: (event.touches[0].clientY - this._img_top)
}, {
x: (event.touches[1].clientX - this._img_left),
y: (event.touches[1].clientY - this._img_top)
}]
if (!this.disable_rotate) {
let first_atan = 180 / Math.PI * Math.atan2(_touch_img_relative[0].y, _touch_img_relative[0].x)
let first_atan_old = 180 / Math.PI * Math.atan2(this._touch_img_relative[0].y, this._touch_img_relative[0].x)
let second_atan = 180 / Math.PI * Math.atan2(_touch_img_relative[1].y, _touch_img_relative[1].x)
let second_atan_old = 180 / Math.PI * Math.atan2(this._touch_img_relative[1].y, this._touch_img_relative[1].x)
// 当前旋转的角度
let first_deg = first_atan - first_atan_old,
second_deg = second_atan - second_atan_old
if (first_deg != 0) {
current_deg = first_deg
} else if (second_deg != 0) {
current_deg = second_deg
}
}
this._touch_img_relative = _touch_img_relative
this._hypotenuse_length = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))
this.angle = this.angle + current_deg
this.scale = this.scale
}
!this._canvas_overflow && this._draw()
},
// 结束操作
_end(event) {
this._flag_img_endtouch = true
this._moveStop()
// console.log(event)
},
hideCut(e) {
this.chooseShow = e.chooseShow
},
imgSrcCut(e) {
this.imgSrc = e.imgSrc
},
/**
* 上传图片
*/
upload() {
let that = this
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res) {
const tempFilePaths = res.tempFilePaths[0]
console.log('Img Temp Paths: ' + tempFilePaths)
that.pushImg(tempFilePaths)
wx.showLoading({
title: '加载中...'
})
}
})
},
/**
* 返回图片信息
*/
getImg(getCallback) {
this._draw(() => {
wx.canvasToTempFilePath({
width: this.width * this.export_scale,
height: Math.round(this.height * this.export_scale),
destWidth: this.width * this.export_scale,
destHeight: Math.round(this.height) * this.export_scale,
fileType: 'png',
quality: this.quality,
canvasId: this.el,
success: (res) => {
getCallback({
url: res.tempFilePath,
width: this.width * this.export_scale,
height: this.height * this.export_scale
})
}
}, this)
})
},
/**
* 设置图片动画
* {
* x:10,//图片在原有基础上向下移动10px
* y:10,//图片在原有基础上向右移动10px
* angle:10,//图片在原有基础上旋转10deg
* scale:0.5,//图片在原有基础上增加0.5倍
* }
*/
setTransform(transform) {
if (!transform) return
if (!this.disable_rotate) {
this.angle = transform.angle ? this.angle + transform.angle : this.angle
}
var scale = this.scale
if (transform.scale) {
scale = this.scale + transform.scale
scale = scale <= this.min_scale ? this.min_scale : scale
scale = scale >= this.max_scale ? this.max_scale : scale
}
this.scale = scale
let cutX = this.cut_left
let cutY = this.cut_top
if (transform.cutX) {
this.cut_left = cutX + transform.cutX
}
if (transform.cutY) {
this.cut_top = cutY + transform.cutY
}
this._img_top = transform.y ? this._img_top + transform.y : this._img_top
this._img_left = transform.x ? this._img_left + transform.x : this._img_left
// 图像边缘检测,防止截取到空白
this._imgMarginDetectionScale()
// 停止居中裁剪框,继续修改图片位置
this._moveDuring()
this.scale = this.scale
this._img_top = this._img_top
this._img_left = this._img_left
!this._canvas_overflow && this._draw()
// 可以居中裁剪框了
this._moveStop()// 结束操作
},
/**
* 设置剪裁框尺寸
*/
setCutSize(w, h) {
this.width = w
this.height = h
this._computeCutSize()
},
/**
* 设置剪裁框和图片居中
*/
setCutCenter() {
let cut_top = (this.info.windowHeight - this.height) * 0.5
let cut_left = (this.info.windowWidth - this.width) * 0.5
// 顺序不能变
this._img_top = this._img_top - this.cut_top + cut_top
this.cut_top = cut_top
this._img_left = this._img_left - this.cut_left + cut_left
this.cut_left = cut_left
},
_setCutCenter() {
let cut_top = (this.info.windowHeight - this.height) * 0.5
let cut_left = (this.info.windowWidth - this.width) * 0.5
this.cut_top = cut_top
this.cut_left = cut_left
},
/**
* 设置剪裁框宽度-即将废弃
*/
setWidth(width) {
this.width = width
this._computeCutSize()
},
/**
* 设置剪裁框高度-即将废弃
*/
setHeight(height) {
this.height = height
this._computeCutSize()
},
/**
* 是否锁定旋转
*/
setDisableRotate(value) {
this.disable_rotate = value
},
/**
* 是否限制移动
*/
setLimitMove(value) {
this._cut_animation = true
this.limit_move = !!value
},
/**
* 初始化图片,包括位置、大小、旋转角度
*/
imgReset() {
this.scale = 1
this.angle = 0
this.tanLeft = 0
this.tanTop = 0
this._img_top = wx.getSystemInfoSync().windowHeight / 2
this._img_left = wx.getSystemInfoSync().windowWidth / 2
},
/**
* 加载(更换)图片
*/
pushImg(src) {
console.log('pushing img: ' + src)
if (src) {
this.imgSrc = src
// 发现是手动赋值直接返回交给watch处理
return
}
wx.getImageInfo({
src: this.imgSrc,
success: (res) => {
console.log('----------------------', res)
this.imageObject = res
// 图片非本地路径需要换成本地路径
if (this.imgSrc.search(/tmp/) == -1) {
this.imgSrc = res.path
}
// 计算最后图片尺寸
this._imgComputeSize()
if (this.limit_move) {
// 限制移动,不留空白处理
this._imgMarginDetectionScale()
}
this._draw()
},
fail: (err) => {
this.imgSrc = ''
console.log(err)
}
})
},
/**
* 设置图片放大缩小
*/
setScale(scale) {
if (!scale) return
this.scale = scale
!this._canvas_overflow && this._draw()
},
/**
* 设置图片旋转角度
*/
setAngle(angle) {
if (!angle) return
this._cut_animation = true
this.angle = angle
this._imgMarginDetectionScale()
!this._canvas_overflow && this._draw()
},
_initCanvas() {
// 初始化canvas
// if (!this.ctx) {
let vm = this
this.ctx = wx.createCanvasContext('image-cropper', this.$wx)
console.log(this.ctx, '=====')
// }
},
/**
* 根据开发者设置的图片目标尺寸计算实际尺寸
*/
_initImageSize() {
// 处理宽高特殊单位 %>px
if (this.INIT_IMGWIDTH && typeof this.INIT_IMGWIDTH === 'string' && this.INIT_IMGWIDTH.indexOf('%') != -1) {
let width = this.INIT_IMGWIDTH.replace('%', '')
this.INIT_IMGWIDTH = this.img_width = this.info.windowWidth / 100 * width
}
if (this.INIT_IMGHEIGHT && typeof this.INIT_IMGHEIGHT === 'string' && this.INIT_IMGHEIGHT.indexOf('%') != -1) {
let height = this.img_height.replace('%', '')
this.INIT_IMGHEIGHT = this.img_height = this.info.windowHeight / 100 * height
}
},
/**
* 检测剪裁框位置是否在允许的范围内(屏幕内)
*/
_cutDetectionPosition() {
let _cutDetectionPositionTop = () => {
// 检测上边距是否在范围内
if (this.cut_top < 0) {
this.cut_top = 0
}
if (this.cut_top > this.info.windowHeight - this.height) {
this.cut_top = this.info.windowHeight - this.height
}
}, _cutDetectionPositionLeft = () => {
// 检测左边距是否在范围内
if (this.cut_left < 0) {
this.cut_left = 0
}
if (this.cut_left > this.info.windowWidth - this.width) {
this.cut_left = this.info.windowWidth - this.width
}
}
// 裁剪框坐标处理如果只写一个参数则另一个默认为0都不写默认居中
if (this.cut_top == null && this.cut_left == null) {
this._setCutCenter()
} else if (this.cut_top != null && this.cut_left != null) {
_cutDetectionPositionTop()
_cutDetectionPositionLeft()
} else if (this.cut_top != null && this.cut_left == null) {
_cutDetectionPositionTop()
this.cut_left = (this.info.windowWidth - this.width) / 2
} else if (this.cut_top == null && this.cut_left != null) {
_cutDetectionPositionLeft()
this.cut_top = (this.info.windowHeight - this.height) / 2
}
},
/**
* 检测canvas位置是否在允许的范围内(屏幕内)如果在屏幕外则不开启实时渲染
* 如果只写一个参数则另一个默认为0都不写默认超出屏幕外
*/
_canvasDetectionPosition() {
if (this.canvas_top == null && this.canvas_left == null) {
this._canvas_overflow = false
this.canvas_top = -5000
this.canvas_left = -5000
} else if (this.canvas_top != null && this.canvas_left != null) {
if (this.canvas_top < -this.height || this.canvas_top > this.info.windowHeight) {
this._canvas_overflow = true
} else {
this._canvas_overflow = false
}
} else if (this.canvas_top != null && this.canvas_left == null) {
this.canvas_left = 0
} else if (this.canvas_top == null && this.canvas_left != null) {
this.canvas_top = 0
if (this.canvas_left < -this.width || this.canvas_left > this.info.windowWidth) {
this._canvas_overflow = true
} else {
this._canvas_overflow = false
}
}
},
/**
* 图片边缘检测-位置
*/
_imgMarginDetectionPosition(scale) {
if (!this.limit_move) return
let left = this._img_left
let top = this._img_top
var scaleV2 = scale || this.scale
let img_width = this.img_width
let img_height = this.img_height
if (this.angle / 90 % 2) {
img_width = this.img_height
img_height = this.img_width
}
left = this.cut_left + img_width * scaleV2 / 2 >= left ? left : this.cut_left + img_width * scaleV2 / 2
left = this.cut_left + this.width - img_width * scaleV2 / 2 <= left ? left : this.cut_left + this.width - img_width * scaleV2 / 2
top = this.cut_top + img_height * scaleV2 / 2 >= top ? top : this.cut_top + img_height * scaleV2 / 2
top = this.cut_top + this.height - img_height * scaleV2 / 2 <= top ? top : this.cut_top + this.height - img_height * scaleV2 / 2
this._img_left = left
this._img_top = top
this.scale = scaleV2
},
/**
* 图片边缘检测-缩放
*/
_imgMarginDetectionScale() {
if (!this.limit_move) return
let scale = this.scale
let img_width = this.img_width
let img_height = this.img_height
if (this.angle / 90 % 2) {
img_width = this.img_height
img_height = this.img_width
}
if (img_width * scale < this.width) {
scale = this.width / img_width
}
if (img_height * scale < this.height) {
scale = Math.max(scale, this.height / img_height)
}
this._imgMarginDetectionPosition(scale)
},
/**
* 计算图片尺寸
*/
_imgComputeSize() {
let img_width = this.img_width,
img_height = this.img_height
if (!this.INIT_IMGHEIGHT && !this.INIT_IMGWIDTH) {
// 默认按图片最小边 = 对应裁剪框尺寸
img_width = this.imageObject.width
img_height = this.imageObject.height
if (img_width / img_height > this.width / this.height) {
img_height = this.height
img_width = this.imageObject.width / this.imageObject.height * img_height
} else {
img_width = this.width
img_height = this.imageObject.height / this.imageObject.width * img_width
}
} else if (this.INIT_IMGHEIGHT && !this.INIT_IMGWIDTH) {
img_width = this.imageObject.width / this.imageObject.height * this.INIT_IMGHEIGHT
} else if (!this.INIT_IMGHEIGHT && this.INIT_IMGWIDTH) {
img_height = this.imageObject.height / this.imageObject.width * this.INIT_IMGWIDTH
}
console.log(img_width)
console.log(img_height)
this.img_width = img_width
this.img_height = img_height
},
// 改变截取框大小
_computeCutSize() {
if (this.width > this.info.windowWidth) {
this.width = his.info.windowWidth
} else if (this.width + this.cut_left > this.info.windowWidth) {
this.cut_left = this.info.windowWidth - this.cut_left
}
if (this.height > this.info.windowHeight) {
this.height = this.info.windowHeight
} else if (this.height + this.cut_top > this.info.windowHeight) {
this.cut_top = this.info.windowHeight - this.cut_top
}
!this._canvas_overflow && this._draw()
},
_move_throttle() {
// 安卓需要节流
if (this.info.platform == 'android') {
clearTimeout(this.MOVE_THROTTLE)
this.MOVE_THROTTLE = setTimeout(() => {
this.MOVE_THROTTLE_FLAG = true
}, 1000 / 40)
return this.MOVE_THROTTLE_FLAG
} else {
this.MOVE_THROTTLE_FLAG = true
}
},
// 停止移动时需要做的操作
_moveStop() {
// 清空之前的自动居中延迟函数并添加最新的
clearTimeout(this.TIME_CUT_CENTER)
this.TIME_CUT_CENTER = setTimeout(() => {
// 动画启动
if (!this._cut_animation) {
this._cut_animation = true
}
this.setCutCenter()
}, 1000)
// 清空之前的背景变化延迟函数并添加最新的
clearTimeout(this.TIME_BG)
this.TIME_BG = setTimeout(() => {
if (this._flag_bright) {
this._flag_bright = false
}
}, 2000)
},
// 移动中
_moveDuring() {
// 清空之前的自动居中延迟函数
clearTimeout(this.TIME_CUT_CENTER)
// 清空之前的背景变化延迟函数
clearTimeout(this.TIME_BG)
// 高亮背景
if (!this._flag_bright) {
this._flag_bright = true
}
}
}
})
</script>
<style lang="less">
.main-box{
width: 100%;
height: 10vh;
position: fixed;
bottom: 0;
left: 0;
z-index: 999999;
padding: 0 10vw;
.main-cancel,.main-save{
background: #d1d1d1;
padding: 8rpx 0;
width: 200rpx;
border-radius: 6rpx;
color: #D92553;
}
.main-save{
color: white;
background: #D92553;
}
}
.image-cropper{
background:rgba(14, 13, 13,.8);
position: fixed;
top:0;
left:0;
width:100vw;
height:100vh;
z-index: 1;
}
.main{
position: absolute;
width:100vw;
height:100vh;
overflow: hidden;
}
.content{
z-index: 9;
position: absolute;
width:100vw;
height:100vh;
display: flex;
flex-direction:column;
pointer-events:none;
}
.bg_black{
background: rgba(0, 0, 0, 0.8)!important;
}
.bg_gray{
background: rgba(0, 0, 0, 0.45);
transition-duration: .35s;
}
.content>.content_top{
pointer-events:none;
}
.content>.content_middle{
display: flex;
height: 200px;
width:100%;
}
.content_middle_middle{
width:200px;
box-sizing:border-box;
position: relative;
transition-duration: .3s;
}
.content_middle_right{
flex: auto;
}
.content>.content_bottom{
flex: auto;
}
.image-cropper .img{
z-index: 2;
top:0;
left:0;
position: absolute;
border:none;
width:100%;
backface-visibility: hidden;
transform-origin:center;
}
.image-cropper-canvas{
position: fixed;
background: white;
width:150px;
height:150px;
z-index: 10;
top:-200%;
pointer-events:none;
}
.border{
background: white;
pointer-events:auto;
position:absolute;
}
.border-top-left{
left:-2.5px;
top:-2.5px;
height:2.5px;
width:33rpx;
}
.border-top-right{
right:-2.5px;
top:-2.5px;
height:2.5px;
width:33rpx;
}
.border-right-top{
top:-1px;
width:2.5px;
height:30rpx;
right:-2.5px;
}
.border-right-bottom{
width:2.5px;
height:30rpx;
right:-2.5px;
bottom:-1px;
}
.border-bottom-left{
height:2.5px;
width:33rpx;
bottom:-2.5px;
left:-2.5px;
}
.border-bottom-right{
height:2.5px;
width:33rpx;
bottom:-2.5px;
right:-2.5px;
}
.border-left-top{
top:-1px;
width:2.5px;
height:30rpx;
left:-2.5px;
}
.border-left-bottom{
width:2.5px;
height:30rpx;
left:-2.5px;
bottom:-1px;
}
</style>
<config>
{
usingComponents: {
chooseImageTips: '~@/components/chooseImageTips',
}
}
</config>