ufutx_love_mp/src/components/wepy-image-cropper.wpy
2024-09-29 11:01:17 +08:00

1293 lines
42 KiB
Plaintext
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.

<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 || !this.ctx) 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 && (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>