您的位置:首页 > 健康 > 美食 > uniapp+canvas实现逐字手写效果

uniapp+canvas实现逐字手写效果

2024/12/21 8:45:23 来源:https://blog.csdn.net/qq_66390045/article/details/139101863  浏览:    关键词:uniapp+canvas实现逐字手写效果

在移动端使用 UniApp 进行逐字手写的功能。用户可以在一个 inputCanvas 上书写单个字,然后在特定时间后将这个字添加到 outputCanvas 上,形成一个逐字的手写效果。用户还可以保存整幅图像或者撤销上一个添加的字。

 

  1. 初始化 Canvas

    • 使用 uni.createCanvasContext 创建画布上下文,设置笔触样式和线条属性。
  2. 触摸事件处理

    • handleTouchStart:捕获触摸开始事件,初始化绘图状态。
    • handleTouchMove:捕获触摸移动事件,实时绘制路径。
    • handleTouchEnd:捕获触摸结束事件,启动定时器准备添加字。
  3. 添加字符

    • addChar 方法将 inputCanvas 的内容绘制到 outputCanvas 上,同时保存字符的路径。
  4. 撤销功能

    • undoChar 方法删除上一个字符,并重新绘制 outputCanvas
  5. 保存和上传图像

    • 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>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com