<template>
<canvas
canvas-id="canvasImage"
id="canvasImage"
:disableScroll="true"
></canvas>
<canvas
canvas-id="canvasBackground"
id="canvasBackground"
:disableScroll="true"
@error="onError"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchend"
>
<cover-view class="canvas-bottom"
><cover-view @tap="onChooseImage">选择图片</cover-view
><cover-view @tap="onCanvasToTempFilePath">确认</cover-view></cover-view
>
</canvas>
</template>
<script lang="ts" setup>
import {
eventCenter,
getCurrentInstance,
createCanvasContext,
getSystemInfoSync,
chooseImage,
getImageInfo,
canvasToTempFilePath,
} from "@tarojs/taro";
import { reactive, ref } from "vue-demi";
// 当前设备宽高数据的接口
interface GetSystem {
screenHeight: number; //宽
screenWidth: number; //高
}
// 当前选择的遮罩的宽高和位置
interface ChoiceBox {
w: number; // 选择框的宽
h: number; // 选择框的高
t: number; // 选择框的头部位置
l: number; // 选择框的左边的位置
}
// 要绘制的图片
interface DrawImageMessage {
path: string; //图片的路径
width: number; //图片的实际宽度
height: number; //图片的实际高度
canvasw: number; //图片绘制在canvas的宽度
canvash: number; //图片绘制在canvas的高度
ratio: number; //图片的宽高比例
y: number; //图片的y轴距离
x: number; //图片的x轴距离
zoomw: number; // 图片缩放的宽
zoomh: number; // 图片缩放的高
dragx: number; //拖动产生的,x轴渲染位置
dragy: number; //拖动产生的,y轴渲染位置
}
// 记录单手指拖动
interface OneFingerDrag {
x: number; //拖动的x轴
y: number; //拖动的y轴
}
let one_finger_drag = reactive<OneFingerDrag>({
x: 0,
y: 0,
});
// 当前设备宽高数据
let getSystemInfoSync_data = reactive<GetSystem>({
screenHeight: 0,
screenWidth: 0,
});
// 当前选择的遮罩的宽高和位置
let choice_box = reactive<ChoiceBox>({
w: 250,
h: 250,
t: 0,
l: 0,
});
let draw_image_message = reactive<DrawImageMessage>({
path: "", //图片的路径
width: 0, //图片的实际宽度
height: 0, //图片的实际高度
canvasw: 0, //图片绘制在canvas的宽度
canvash: 0, //图片绘制在canvas的高度
ratio: 0, //图片的宽高比例
y: 0, //图片渲染位置
x: 0, //图片渲染位置
zoomw: 0, // 图片缩放的宽
zoomh: 0, // 图片缩放的高
dragx: 0, //拖动产生的,x轴渲染位置
dragy: 0, //拖动产生的,y轴渲染位置
});
let initialPosition = ref<number>(0); //手指的初始距离
let record_left = ref<number>(0); //记录当前手指松开次数
// 图片选择
const onChooseImage = (): void => {
chooseImage({
count: 1, //默认9
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
sourceType: ["album", "camera"], //从相册选择
success: (res) => {
getImageInfo({
src: res.tempFilePaths[0],
success: (image) => {
let { width, height, path } = image;
draw_image_message.width = width; //保存当前图片的实际宽度
draw_image_message.ratio = width / height; //保存当前图片的宽高比例
draw_image_message.height = height; //保存当前图片的实际高度
draw_image_message.path = path; //保存当前图片的路径
draw_image_message.canvash = draw_image_message.zoomh =
getSystemInfoSync_data.screenWidth / draw_image_message.ratio; //保存当前图片的渲染高度和缩放高度
draw_image_message.canvasw = draw_image_message.zoomw =
getSystemInfoSync_data.screenWidth; //保存当前图片的渲染宽度和缩放宽度
draw_image_message.x = 0; //保存当前图片的渲染 x轴的位置
draw_image_message.y =
getSystemInfoSync_data.screenHeight / 2 -
draw_image_message.canvash / 2; //保存当前图片的渲染 y轴的位置
canvasImage.drawImage(
//渲染图片
path,
draw_image_message.x,
draw_image_message.y,
draw_image_message.canvasw,
draw_image_message.canvash
);
},
});
},
});
};
// 导出要截取的图片
const onCanvasToTempFilePath = () => {
canvasToTempFilePath({
canvasId: "canvasImage",
destHeight: choice_box.h,
destWidth: choice_box.w,
height: choice_box.h,
width: choice_box.w,
x: choice_box.l,
y: choice_box.t,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
},
});
};
eventCenter.once(getCurrentInstance().router.onReady, () => {
// 获取当前设备的宽高 -s
let getSystem = getSystemInfoSync();
getSystemInfoSync_data.screenWidth = getSystem.screenWidth;
getSystemInfoSync_data.screenHeight = getSystem.screenHeight;
// 获取当前设备的宽高 -s
// 计算选择框的x和y的位置 -s
choice_box.t = getSystem.screenHeight / 2 - choice_box.w / 2; //如果设置了navigationStyle: 'custom', 不需要后面的减法
choice_box.l = getSystem.screenWidth / 2 - choice_box.h / 2;
// 计算选择框的x和y的位置 -s
canvasBackground.init();
canvasImage.init();
});
// 绘制图片
let canvasImage = {
instantiation: null,
init(): void {
this.instantiation = createCanvasContext("canvasImage");
},
drawImage(
path: string,
dx: number = 0,
dy: number = 0,
dWidth: number = 0,
dHeight: number = 0
): void {
this.instantiation.drawImage(path, dx, dy, dWidth, dHeight);
this.instantiation.draw();
},
};
// 绘制背景图的 canvas 组件 -s
let canvasBackground = {
instantiation: null,
init(): void {
this.instantiation = createCanvasContext("canvasBackground");
this.fillAvatarCropperBgLucencyColor();
},
// 绘制透明遮罩层
fillAvatarCropperBgLucencyColor(): void {
this.instantiation.setFillStyle("rgba(0,0,0,.2)");
this.instantiation.fillRect(
0,
0,
getSystemInfoSync_data.screenWidth,
choice_box.t
);
this.instantiation.fillRect(
0,
choice_box.t,
choice_box.l,
getSystemInfoSync_data.screenHeight
);
this.instantiation.fillRect(
choice_box.l + choice_box.w,
choice_box.t,
choice_box.l,
getSystemInfoSync_data.screenHeight
);
this.instantiation.fillRect(
choice_box.l,
choice_box.t + choice_box.w,
choice_box.w,
getSystemInfoSync_data.screenHeight
);
this.instantiation.save();
this.instantiation.draw({ reserve: true });
this.strokeRect();
},
// 绘制一个矩形
strokeRect(): void {
this.instantiation.setStrokeStyle("#fff");
this.instantiation.strokeRect(
choice_box.l,
choice_box.t,
choice_box.w,
choice_box.h
);
this.instantiation.draw({ reserve: true });
},
};
// 绘制背景图的 canvas 组件 -e
// 操作
//错误信息
const onError = (err: any): void => {
console.log(err);
};
// 手指触摸动作结束
const onTouchend = (): void => {
// 保存拖动后的值
draw_image_message.x = draw_image_message.dragx;
draw_image_message.y = draw_image_message.dragy;
// 记录当前手指松开次数,用于判断当前的操作是缩放还是拖动,避免,缩放后,最后松开手指时,会照成拖动动作
record_left.value += 1;
if (initialPosition.value > 0) {
if (record_left.value === 2) {
// 清除两个手指移动的距离
initialPosition.value = 0;
record_left.value = 0;
}
} else {
record_left.value = 0;
}
};
// 手指触摸动作开始
const onTouchStart = (e: any): void => {
if (
start.hasOwnProperty("touch" + e.touches.length) &&
draw_image_message.path.length > 1
) {
// 分配单手指和双手指的处理方式
start["touch" + e.touches.length](e);
}
};
// 手指拖动
const onTouchMove = (e: any): void => {
if (
move.hasOwnProperty("touch" + e.touches.length) &&
draw_image_message.path.length > 1
) {
// 分配单手指和双手指的处理方式
move["touch" + e.touches.length](e);
}
};
// 计算两个手指之间的距离
const getNewScale = (touch0: any, touch1: any): number => {
let xMove: number, yMove: number;
// 计算两指距离
xMove = Math.round(touch1.x - touch0.x);
yMove = Math.round(touch1.y - touch0.y);
return Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
};
// 手指的处理方式
interface Touch {
touch1: (e: any) => void; //单手指拖动处理
touch2: (e: any) => void; //双手指拖动处理
}
// 手指的拖动的处理方式
const move: Touch = {
/**
* 双手指拖动处理
* @param e 事件模型
*/
touch2(e: any): void {
let newDistance: number = getNewScale(e.touches[0], e.touches[1]);
let coefficient: number =
1.5 + 0.001 * 8 * (newDistance - initialPosition.value);
// 设定缩放范围
coefficient <= 1 && (coefficient = 1);
coefficient >= 2.5 && (coefficient = 2.5);
let imagew: number = Math.round(coefficient * draw_image_message.canvasw);
let imageh: number = Math.round(coefficient * draw_image_message.canvash);
// 计算图片渲染位置
let x: number = draw_image_message.x;
let y: number = draw_image_message.y;
// 保存当前图片的渲染宽高
draw_image_message.zoomw = imagew;
draw_image_message.zoomh = imageh;
// 计算图片x轴的范围
if (x + draw_image_message.zoomw <= choice_box.l + choice_box.w) {
x = choice_box.l + choice_box.w - draw_image_message.zoomw;
}
// 计算图片y轴的范围
if (y + draw_image_message.zoomh <= choice_box.h + choice_box.t) {
y = choice_box.h + choice_box.t - draw_image_message.zoomh;
}
// 保存当前图片渲染的位置
draw_image_message.dragx = x;
draw_image_message.dragy = y;
// 绘制图片
canvasImage.drawImage(
draw_image_message.path,
x,
y,
draw_image_message.zoomw,
draw_image_message.zoomh
);
},
/**
* 单手指拖动处理
* @param e 事件模型
*/
touch1(e: any): void {
if (initialPosition.value > 0) return; // 如果当前initialPosition值大于零,表示当前是两个手指在做缩放动作
let { x, y } = e.touches[0];
let dx = Math.round(draw_image_message.x - (one_finger_drag.x - x));
let dy = Math.round(draw_image_message.y - (one_finger_drag.y - y));
// 判断图片在 x轴移动的位置限制
if (dx >= choice_box.l) {
dx = choice_box.l;
} else if (dx <= choice_box.l + choice_box.w - draw_image_message.zoomw) {
dx = choice_box.l + choice_box.w - draw_image_message.zoomw;
}
// 判断图片在y轴移动的位置判断
if (dy >= choice_box.t) {
dy = choice_box.t;
} else if (dy <= choice_box.t + choice_box.h - draw_image_message.zoomh) {
dy = choice_box.t + choice_box.h - draw_image_message.zoomh;
}
// 保存移动后图片渲染的位置
draw_image_message.dragx = dx;
draw_image_message.dragy = dy;
// 绘制图片
canvasImage.drawImage(
draw_image_message.path,
dx,
dy,
draw_image_message.zoomw,
draw_image_message.zoomh
);
},
};
// 手指初始化位置
const start: Touch = {
/**
* 单手指处理
* @param e 事件模型
*/
touch1(e: any): void {
one_finger_drag.x = e.touches[0].x;
one_finger_drag.y = e.touches[0].y;
},
/**
* 双手指处理
* @param e 事件模型
*/
touch2(e: any): void {
let oldDistance: number = getNewScale(e.touches[0], e.touches[1]);
initialPosition.value = oldDistance;
},
};
</script>
<style>
page {
background-color: #000;
}
</style>
<style lang="scss">
#canvasBackground,
#canvasImage {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
z-index: 11;
}
.canvas-bottom {
width: calc(100vw - 40rpx);
height: 100rpx;
background-color: #000;
position: fixed;
bottom: 0;
left: 0;
color: #ffffff;
padding: 0 20rpx;
display: flex;
justify-content: flex-start;
align-items: center;
justify-content: space-between;
}
</style>