当前位置:   article > 正文

uniapp+canvas实现逐字手写效果_小程序手写汉字笔顺 功能怎么实现

小程序手写汉字笔顺 功能怎么实现

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

 

  1. 初始化 Canvas

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

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

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

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

    • saveImage 方法将 outputCanvas 的内容保存为图片,并调用 upload 方法上传。

完整代码:

  1. <template>
  2. <view class="container">
  3. <view class="tip">
  4. <view class="">
  5. 请您在区域内逐字手写以下文字,全部写完后点击保存!
  6. </view>
  7. <u-alert style="margin-bottom: 20upx;" :description="ruleForm.sqcn" type = "primary" ></u-alert>
  8. </view>
  9. <view class="canvas-container">
  10. <canvas canvas-id="inputCanvas" class="input-canvas" @touchstart="handleTouchStart"
  11. @touchmove="handleTouchMove" @touchend="handleTouchEnd"></canvas>
  12. </view>
  13. <view class="buttons">
  14. <u-button text="撤销上一个字" size="normal" type="error" @click="undoChar"></u-button>
  15. <u-button text="保存" size="normal" type="primary" @click="saveImage"></u-button>
  16. </view>
  17. <canvas :style="{ height: outputHeight }" canvas-id="outputCanvas" class="output-canvas"></canvas>
  18. </view>
  19. </template>
  20. <script>
  21. import fileService from "@/api/file/fileService.js";
  22. import knsService from "@/api/kns/knsService"
  23. export default {
  24. data() {
  25. return {
  26. isDrawing: false,
  27. startX: 0,
  28. startY: 0,
  29. strokes: [],
  30. canvasWidth: 300,
  31. canvasHeight: 300,
  32. charObjects: [],
  33. timer: null,
  34. delay: 1000, // 1秒延迟
  35. fj: '',
  36. outputHeight: '50px',
  37. label: '',
  38. ruleForm: {}
  39. };
  40. },
  41. mounted() {
  42. this.getData()
  43. this.initCanvas('inputCanvas');
  44. this.initCanvas('outputCanvas');
  45. },
  46. onLoad(option) {
  47. this.label = option.label;
  48. },
  49. methods: {
  50. // 获取承诺
  51. async getData() {
  52. const res = await knsService.getSettingData();
  53. this.ruleForm = res[0];
  54. },
  55. initCanvas(canvasId) {
  56. const context = uni.createCanvasContext(canvasId, this);
  57. context.setStrokeStyle('#000');
  58. context.setLineWidth(4);
  59. context.setLineCap('round');
  60. context.setLineJoin('round');
  61. context.draw();
  62. },
  63. handleTouchStart(e) {
  64. e.preventDefault(); // 阻止默认滚动行为
  65. if (this.timer) {
  66. clearTimeout(this.timer);
  67. this.timer = null;
  68. }
  69. const touch = e.touches[0];
  70. this.isDrawing = true;
  71. this.startX = touch.x;
  72. this.startY = touch.y;
  73. this.strokes.push({
  74. x: touch.x,
  75. y: touch.y
  76. });
  77. },
  78. handleTouchMove(e) {
  79. e.preventDefault(); // 阻止默认滚动行为
  80. if (!this.isDrawing) return;
  81. const touch = e.touches[0];
  82. const context = uni.createCanvasContext('inputCanvas', this);
  83. context.moveTo(this.startX, this.startY);
  84. context.lineTo(touch.x, touch.y);
  85. context.stroke();
  86. context.draw(true);
  87. this.startX = touch.x;
  88. this.startY = touch.y;
  89. this.strokes.push({
  90. x: touch.x,
  91. y: touch.y
  92. });
  93. },
  94. handleTouchEnd(e) {
  95. e.preventDefault(); // 阻止默认滚动行为
  96. this.isDrawing = false;
  97. this.timer = setTimeout(this.addChar, this.delay);
  98. },
  99. addChar() {
  100. const inputContext = uni.createCanvasContext('inputCanvas', this);
  101. uni.canvasToTempFilePath({
  102. canvasId: 'inputCanvas',
  103. success: (res) => {
  104. // 保存这个字符的路径
  105. this.charObjects.push(res.tempFilePath);
  106. // 清空 inputCanvas 上的内容
  107. inputContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  108. inputContext.draw();
  109. this.redrawOutputCanvas()
  110. },
  111. });
  112. },
  113. undoChar() {
  114. if (this.charObjects.length > 0) {
  115. this.charObjects.pop();
  116. this.redrawOutputCanvas();
  117. if (this.charObjects.length === 0) {
  118. this.outputHeight = 50; // 如果字符对象为空,则将输出画布高度设置为 50
  119. }
  120. }
  121. },
  122. redrawOutputCanvas() {
  123. const context = uni.createCanvasContext('outputCanvas', this);
  124. const charSize = 50; // 调整字符大小
  125. const charSpacing = 48; // 调整字符间距
  126. const maxCharsPerRow = Math.floor(this.canvasWidth / charSpacing); // 每行最大字符数
  127. // 动态设置高度
  128. const numRows = Math.ceil(this.charObjects.length / maxCharsPerRow); // 计算行数
  129. this.outputHeight = `${numRows * charSize}px`; // 动态计算输出画布的高度
  130. console.log(this.outputHeight, this.charObjects.length, 'outputHeight');
  131. // 清除画布并设置高度
  132. context.clearRect(0, 0, this.canvasWidth, this.outputHeight);
  133. // 绘制字符
  134. this.charObjects.forEach((charPath, index) => {
  135. const rowIndex = Math.floor(index / maxCharsPerRow); // 当前字符的行索引
  136. const colIndex = index % maxCharsPerRow; // 当前字符的列索引
  137. context.drawImage(charPath, 10 + colIndex * charSpacing, 10 + rowIndex * charSpacing, charSize,
  138. charSize);
  139. });
  140. this.$nextTick(() => {
  141. // 一次性绘制所有字符
  142. context.draw();
  143. })
  144. },
  145. saveImage() {
  146. if (this.charObjects.length === 0) {
  147. uni.showToast({
  148. icon: "error",
  149. title: '请手写文字!'
  150. })
  151. return false;
  152. }
  153. uni.canvasToTempFilePath({
  154. canvasId: 'outputCanvas',
  155. success: (res) => {
  156. // 保存图片
  157. console.log(res.tempFilePath, 'res.tempFilePath');
  158. this.upload(res.tempFilePath);
  159. },
  160. });
  161. },
  162. upload(img) {
  163. fileService.upload(img).then((res) => {
  164. let pages = getCurrentPages()
  165. let currPage = pages[pages.length - 1]; //当前页面
  166. let prevPage = pages[pages.length - 2]; //上一个页面
  167. //修改前一页数据
  168. if (prevPage.inputForm) {
  169. prevPage.inputForm[this.label] = res
  170. }
  171. console.log(res, 'res');
  172. //返回上一页
  173. uni.navigateBack({
  174. delta: 1,
  175. })
  176. });
  177. },
  178. },
  179. };
  180. </script>
  181. <style scoped lang="scss">
  182. .container {
  183. display: flex;
  184. flex-direction: column;
  185. align-items: center;
  186. margin-top: 40upx;
  187. .canvas-container {
  188. position: relative;
  189. width: 600upx;
  190. height: 600upx;
  191. .input-canvas {
  192. position: absolute;
  193. top: 0;
  194. left: 0;
  195. width: 100%;
  196. height: 100%;
  197. border-radius: 10upx;
  198. border: 4upx dashed #dddee1;
  199. touch-action: none;
  200. /* 禁止默认触摸动作 */
  201. }
  202. }
  203. .output-canvas {
  204. width: 600upx;
  205. /* 设置高度为原来的一半 */
  206. border: 2upx solid #dddee1;
  207. margin-top: 40upx;
  208. }
  209. .buttons {
  210. display: flex;
  211. justify-content: space-around;
  212. width: 100%;
  213. padding: 0upx 50upx;
  214. }
  215. button {
  216. margin: 20upx;
  217. }
  218. .tip {
  219. view:nth-child(1){
  220. color: #FF6F77;
  221. font-size: 24upx;
  222. margin-bottom: 20upx;
  223. }
  224. }
  225. }
  226. </style>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/626600
推荐阅读
相关标签
  

闽ICP备14008679号