赞
踩
在移动端使用 UniApp 进行逐字手写的功能。用户可以在一个 inputCanvas
上书写单个字,然后在特定时间后将这个字添加到 outputCanvas
上,形成一个逐字的手写效果。用户还可以保存整幅图像或者撤销上一个添加的字。
初始化 Canvas:
uni.createCanvasContext
创建画布上下文,设置笔触样式和线条属性。触摸事件处理:
handleTouchStart
:捕获触摸开始事件,初始化绘图状态。handleTouchMove
:捕获触摸移动事件,实时绘制路径。handleTouchEnd
:捕获触摸结束事件,启动定时器准备添加字。添加字符:
addChar
方法将 inputCanvas
的内容绘制到 outputCanvas
上,同时保存字符的路径。撤销功能:
undoChar
方法删除上一个字符,并重新绘制 outputCanvas
。保存和上传图像:
saveImage
方法将 outputCanvas
的内容保存为图片,并调用 upload
方法上传。- <template>
- <view class="container">
- <view class="tip">
- <view class="">
- 请您在区域内逐字手写以下文字,全部写完后点击保存!
- </view>
-
- <u-alert style="margin-bottom: 20upx;" :description="ruleForm.sqcn" type = "primary" ></u-alert>
- </view>
- <view class="canvas-container">
- <canvas canvas-id="inputCanvas" class="input-canvas" @touchstart="handleTouchStart"
- @touchmove="handleTouchMove" @touchend="handleTouchEnd"></canvas>
- </view>
- <view class="buttons">
- <u-button text="撤销上一个字" size="normal" type="error" @click="undoChar"></u-button>
- <u-button text="保存" size="normal" type="primary" @click="saveImage"></u-button>
- </view>
- <canvas :style="{ height: outputHeight }" canvas-id="outputCanvas" class="output-canvas"></canvas>
- </view>
- </template>
-
- <script>
- import fileService from "@/api/file/fileService.js";
- import knsService from "@/api/kns/knsService"
- export default {
- data() {
- return {
- isDrawing: false,
- startX: 0,
- startY: 0,
- strokes: [],
- canvasWidth: 300,
- canvasHeight: 300,
- charObjects: [],
- timer: null,
- delay: 1000, // 1秒延迟
- fj: '',
- outputHeight: '50px',
- label: '',
- ruleForm: {}
- };
- },
- mounted() {
- this.getData()
- this.initCanvas('inputCanvas');
- this.initCanvas('outputCanvas');
- },
- onLoad(option) {
- this.label = option.label;
- },
- methods: {
- // 获取承诺
- async getData() {
- const res = await knsService.getSettingData();
- this.ruleForm = res[0];
- },
- initCanvas(canvasId) {
- const context = uni.createCanvasContext(canvasId, this);
- context.setStrokeStyle('#000');
- context.setLineWidth(4);
- context.setLineCap('round');
- context.setLineJoin('round');
- context.draw();
- },
- handleTouchStart(e) {
- e.preventDefault(); // 阻止默认滚动行为
- if (this.timer) {
- clearTimeout(this.timer);
- this.timer = null;
- }
- const touch = e.touches[0];
- this.isDrawing = true;
- this.startX = touch.x;
- this.startY = touch.y;
- this.strokes.push({
- x: touch.x,
- y: touch.y
- });
- },
- handleTouchMove(e) {
- e.preventDefault(); // 阻止默认滚动行为
- if (!this.isDrawing) return;
- const touch = e.touches[0];
- const context = uni.createCanvasContext('inputCanvas', this);
- context.moveTo(this.startX, this.startY);
- context.lineTo(touch.x, touch.y);
- context.stroke();
- context.draw(true);
- this.startX = touch.x;
- this.startY = touch.y;
- this.strokes.push({
- x: touch.x,
- y: touch.y
- });
- },
- handleTouchEnd(e) {
- e.preventDefault(); // 阻止默认滚动行为
- this.isDrawing = false;
- this.timer = setTimeout(this.addChar, this.delay);
- },
- addChar() {
- const inputContext = uni.createCanvasContext('inputCanvas', this);
- uni.canvasToTempFilePath({
- canvasId: 'inputCanvas',
- success: (res) => {
- // 保存这个字符的路径
- this.charObjects.push(res.tempFilePath);
- // 清空 inputCanvas 上的内容
- inputContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
- inputContext.draw();
- this.redrawOutputCanvas()
- },
- });
- },
- undoChar() {
- if (this.charObjects.length > 0) {
- this.charObjects.pop();
- this.redrawOutputCanvas();
- if (this.charObjects.length === 0) {
- this.outputHeight = 50; // 如果字符对象为空,则将输出画布高度设置为 50
- }
- }
- },
- redrawOutputCanvas() {
- const context = uni.createCanvasContext('outputCanvas', this);
-
- const charSize = 50; // 调整字符大小
- const charSpacing = 48; // 调整字符间距
- const maxCharsPerRow = Math.floor(this.canvasWidth / charSpacing); // 每行最大字符数
-
- // 动态设置高度
- const numRows = Math.ceil(this.charObjects.length / maxCharsPerRow); // 计算行数
- this.outputHeight = `${numRows * charSize}px`; // 动态计算输出画布的高度
- console.log(this.outputHeight, this.charObjects.length, 'outputHeight');
-
- // 清除画布并设置高度
- context.clearRect(0, 0, this.canvasWidth, this.outputHeight);
- // 绘制字符
-
- this.charObjects.forEach((charPath, index) => {
- const rowIndex = Math.floor(index / maxCharsPerRow); // 当前字符的行索引
- const colIndex = index % maxCharsPerRow; // 当前字符的列索引
- context.drawImage(charPath, 10 + colIndex * charSpacing, 10 + rowIndex * charSpacing, charSize,
- charSize);
- });
- this.$nextTick(() => {
- // 一次性绘制所有字符
- context.draw();
- })
- },
-
-
-
- saveImage() {
- if (this.charObjects.length === 0) {
- uni.showToast({
- icon: "error",
- title: '请手写文字!'
- })
- return false;
- }
-
- uni.canvasToTempFilePath({
- canvasId: 'outputCanvas',
- success: (res) => {
- // 保存图片
- console.log(res.tempFilePath, 'res.tempFilePath');
- this.upload(res.tempFilePath);
- },
- });
- },
- upload(img) {
- fileService.upload(img).then((res) => {
- let pages = getCurrentPages()
- let currPage = pages[pages.length - 1]; //当前页面
- let prevPage = pages[pages.length - 2]; //上一个页面
-
- //修改前一页数据
- if (prevPage.inputForm) {
- prevPage.inputForm[this.label] = res
- }
-
- console.log(res, 'res');
- //返回上一页
- uni.navigateBack({
- delta: 1,
- })
- });
- },
- },
- };
- </script>
-
- <style scoped lang="scss">
- .container {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-top: 40upx;
-
- .canvas-container {
- position: relative;
- width: 600upx;
- height: 600upx;
- .input-canvas {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- border-radius: 10upx;
- border: 4upx dashed #dddee1;
- touch-action: none;
- /* 禁止默认触摸动作 */
- }
- }
-
-
-
- .output-canvas {
- width: 600upx;
- /* 设置高度为原来的一半 */
- border: 2upx solid #dddee1;
- margin-top: 40upx;
- }
-
- .buttons {
- display: flex;
- justify-content: space-around;
- width: 100%;
- padding: 0upx 50upx;
- }
-
- button {
- margin: 20upx;
- }
-
- .tip {
- view:nth-child(1){
- color: #FF6F77;
- font-size: 24upx;
- margin-bottom: 20upx;
- }
- }
- }
- </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。