refactor: ♻️ Cropper组件支持支付宝小程序

This commit is contained in:
xuqingkai 2023-06-26 23:23:41 +08:00
parent 0b2df6e7b2
commit cd03fa292d
4 changed files with 121 additions and 49 deletions

View File

@ -1,3 +1,12 @@
<!--
* @Author: weisheng
* @Date: 2023-06-13 11:47:12
* @LastEditTime: 2023-06-26 23:22:26
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\pages\imgCropper\Index.vue
* 记得注释
-->
<template>
<demo-block title="基本用法" style="text-align: center">
<wd-img-cropper
@ -28,16 +37,10 @@ const show = ref<boolean>(false)
function upload() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res) {
success: (res) => {
const tempFilePaths = res.tempFilePaths[0]
src.value = tempFilePaths
show.value = true
// that.setData({
// show: true,
// src: tempFilePaths
// })
}
})
}

View File

@ -329,3 +329,20 @@ export function deepMerge(target = {}, source = {}) {
}
return target
}
/**
*
* @param path
* @param params
* @returns
*/
export function setUrlParams(path: string, params: Record<string, string>) {
for (const key in params) {
if (path.indexOf('?') > -1) {
path = path + `&${key}=${params[key]}`
} else {
path = path + `?${key}=${params[key]}`
}
}
return path
}

View File

@ -1,5 +1,5 @@
<template>
<view @click="handleClick" :class="rootClass" :style="rootStyle">
<view @click="handleClick" data-eventsync="true" :class="rootClass" :style="rootStyle">
<image v-if="isImageUrl" class="wd-icon__image" :src="name"></image>
</view>
</template>

View File

@ -1,4 +1,5 @@
<template>
<!-- 绘制的图片canvas -->
<view id="wd-img-cropper" v-if="modelValue" :class="`wd-img-cropper ${customClass}`" @touchmove="preventTouchMove">
<!-- 展示在用户面前的裁剪框 -->
<view class="wd-img-cropper__wrapper">
@ -32,11 +33,11 @@
</view>
<!-- 展示的传过来的图片: 控制图片的旋转角度(rotate)缩放程度(imgScale)移动位置(translate) -->
<image
:prop="isAnimation"
:change:prop="animation ? animation.setAnimation : ''"
class="wd-img-cropper__img"
:src="imgSrc"
:style="`width: ${picWidth ? picWidth + 'px' : 'auto'}; height: ${picHeight ? picHeight + 'px' : 'auto'}; transform: translate(${
imgLeft - picWidth / 2
}px, ${imgTop - picHeight / 2}px) scale(${imgScale}) rotate(${imgAngle}deg); transition-duration:${isAnimation ? 0.4 : 0}s`"
:style="imageStyle"
:lazy-load="false"
@touchstart="handleImgTouchStart"
@touchmove="handleImgTouchMove"
@ -48,13 +49,14 @@
<!-- 绘制的图片canvas -->
<canvas
canvas-id="wd-img-cropper-canvas"
id="wd-img-cropper-canvas"
class="wd-img-cropper__canvas"
disable-scroll="true"
:disable-scroll="true"
:style="`width: ${Number(canvasWidth) * canvasScale}px; height: ${Number(canvasHeight) * canvasScale}px;`"
/>
<!-- 下方按钮 -->
<view class="wd-img-cropper__footer">
<wd-icon v-if="!disabledRotate" name="rotate" size="24px" color="#fff" @click="handleRotate"></wd-icon>
<wd-icon v-if="!disabledRotate" name="rotate" size="24px" color="#fff" data-eventsync="true" @click="handleRotate"></wd-icon>
<view class="wd-img-cropper__footer--button">
<view class="is-cancel" @click="handleCancel">{{ cancelButtonText }}</view>
<wd-button size="small" :custom-style="buttonStyle" @click="handleConfirm">{{ confirmButtonText }}</wd-button>
@ -65,7 +67,7 @@
<script lang="ts" setup>
import { computed, getCurrentInstance, ref, watch } from 'vue'
import { objToStyle } from '../common/util'
import { addUnit, objToStyle } from '../common/util'
//
let CHANGE_TIME: any | null = null
@ -230,15 +232,15 @@ watch(
immediate: true
}
)
watch(
() => isAnimation.value,
(newValue) => {
// 300
clearTimeout(CHANGE_TIME)
CHANGE_TIME && clearTimeout(CHANGE_TIME)
if (newValue) {
CHANGE_TIME = setTimeout(() => {
isAnimation.value = false
revertIsAnimation(false)
clearTimeout(CHANGE_TIME)
}, 300)
}
},
@ -261,15 +263,34 @@ const buttonStyle = computed(() => {
return objToStyle(style)
})
const imageStyle = computed(() => {
const style: Record<string, string | number> = {
width: picWidth.value ? addUnit(picWidth.value) : 'auto',
height: picHeight.value ? addUnit(picHeight.value) : 'auto',
transform: `translate(${addUnit(imgLeft.value - picWidth.value / 2)}, ${addUnit(imgTop.value - picHeight.value / 2)}) scale(${
imgScale.value
}) rotate(${imgAngle.value}deg)`,
'transition-duration': (isAnimation.value ? 0.4 : 0) + 's'
}
return objToStyle(style)
})
const emit = defineEmits(['imgloaded', 'imgloaderror', 'cancel', 'confirm', 'update:modelValue'])
/**
* 逆转是否使用动画
*/
function revertIsAnimation(animation: boolean) {
isAnimation.value = animation
}
/**
* @description 对外暴露控制旋转角度
* @param {Number} angle 角度
*/
function setRoate(angle: number) {
if (!angle || props.disabledRotate) return
isAnimation.value = true
revertIsAnimation(true)
imgAngle.value = angle
//
detectImgPosIsEdge()
@ -382,28 +403,30 @@ function detectImgPosIsEdge(scale?: number) {
const currentScale = scale || imgScale.value
let currentImgLeft = imgLeft.value
let currentImgTop = imgTop.value
let currentPicWidth = picWidth.value
let currentPicHeight = picHeight.value
//
if ((currentScale / 90) % 2) {
picWidth.value = picHeight.value
picHeight.value = picWidth.value
if ((imgAngle.value / 90) % 2) {
currentPicWidth = picHeight.value
currentPicHeight = picWidth.value
}
//
currentImgLeft =
(picWidth.value * currentScale) / 2 + cutLeft.value >= currentImgLeft ? currentImgLeft : (picWidth.value * imgScale.value) / 2 + cutLeft.value
(currentPicWidth * currentScale) / 2 + cutLeft.value >= currentImgLeft ? currentImgLeft : (currentPicWidth * imgScale.value) / 2 + cutLeft.value
//
currentImgLeft =
cutLeft.value + cutWidth.value - (picWidth.value * currentScale) / 2 <= currentImgLeft
cutLeft.value + cutWidth.value - (currentPicWidth * currentScale) / 2 <= currentImgLeft
? currentImgLeft
: cutLeft.value + cutWidth.value - (picWidth.value * currentScale) / 2
: cutLeft.value + cutWidth.value - (currentPicWidth * currentScale) / 2
//
currentImgTop =
(picHeight.value * currentScale) / 2 + cutTop.value >= currentImgTop ? currentImgTop : (picHeight.value * currentScale) / 2 + cutTop.value
(currentPicHeight * currentScale) / 2 + cutTop.value >= currentImgTop ? currentImgTop : (currentPicHeight * currentScale) / 2 + cutTop.value
//
currentImgTop =
cutTop.value + cutHeight.value - (picHeight.value * currentScale) / 2 <= currentImgTop
cutTop.value + cutHeight.value - (currentPicHeight * currentScale) / 2 <= currentImgTop
? currentImgTop
: cutTop.value + cutHeight.value - (picHeight.value * currentScale) / 2
: cutTop.value + cutHeight.value - (currentPicHeight * currentScale) / 2
imgScale.value = currentScale
imgTop.value = currentImgTop
@ -541,6 +564,7 @@ function handleConfirm(event) {
*/
function canvasToImage() {
const { fileType, quality, exportScale } = props
try {
uni.canvasToTempFilePath(
{
width: cutWidth.value * exportScale,
@ -550,20 +574,22 @@ function canvasToImage() {
fileType,
quality,
canvasId: 'wd-img-cropper-canvas',
success(res) {
emit('update:modelValue', false)
success: (res) => {
emit('confirm', {
tempFilePath: res.tempFilePath,
width: cutWidth.value * exportScale,
height: cutHeight.value * exportScale
})
},
fail() {
complete: () => {
emit('update:modelValue', false)
}
},
proxy
)
} catch (error) {
console.log(error)
}
}
/**
@ -587,6 +613,9 @@ function draw() {
}
// drawImage xyx y
ctx.value!.drawImage(props.imgSrc, -width / 2, -height / 2, width, height)
ctx.value!.restore()
//
ctx.value!.draw(false, () => {
canvasToImage()
@ -598,8 +627,31 @@ function draw() {
draw()
}
function preventTouchMove() {}
defineExpose({
revertIsAnimation
})
</script>
<!-- #ifdef MP-WEIXIN || MP-QQ -->
<script module="animation" lang="wxs">
function setAnimation(newValue, oldValue, ownerInstance){
if (newValue) {
var id = ownerInstance.setTimeout(function() {
ownerInstance.callMethod('revertIsAnimation',false)
ownerInstance.clearTimeout(id)
},300)
}
}
module.exports= {
setAnimation:setAnimation,
}
</script>
<!-- #endif -->
<style lang="scss" scoped>
@import './index.scss';
</style>