JS v0.0.0

  1. <template>
  2. <div class="signatureBox">
  3. <div class="signature-title">签名</div>
  4. <div ref="canvasHW" class="canvasBox">
  5. <canvas
  6. ref="canvasF"
  7. @touchstart="touchStart"
  8. @touchmove="touchMove"
  9. @touchend="touchEnd"
  10. @mousedown="mouseDown"
  11. @mousemove="mouseMove"
  12. @mouseup="mouseUp"
  13. ></canvas>
  14. </div>
  15. <div class="signature-btn">
  16. <cube-button class="btn" type="info" @click.native.prevent="handleGoBack">
  17. 返回
  18. </cube-button>
  19. <cube-button
  20. class="btn"
  21. type="info"
  22. @click.native.prevent="handleOverwrite"
  23. >
  24. 重签
  25. </cube-button>
  26. <cube-button class="btn" type="info" @click.native.prevent="handleSubmit">
  27. 确认
  28. </cube-button>
  29. </div>
  30. </div>
  31. </template>
  32. <script>
  33. export default {
  34. name: 'Signature',
  35. data() {
  36. return {
  37. points: [],
  38. canvasTxt: null,
  39. stageInfo: [],
  40. startX: 0,
  41. startY: 0,
  42. moveY: 0,
  43. moveX: 0,
  44. isDown: false,
  45. strokeStyle: '#000',
  46. lineWidth: 2,
  47. };
  48. },
  49. mounted() {
  50. this.initCanvas();
  51. },
  52. methods: {
  53. initCanvas() {
  54. const canvas = this.$refs.canvasF;
  55. // 获取画布的高度
  56. canvas.height = this.$refs.canvasHW.offsetHeight - 20;
  57. // 获取画布的宽度
  58. canvas.width = this.$refs.canvasHW.offsetWidth - 20;
  59. // 创建 context 对象
  60. this.canvasTxt = canvas.getContext('2d');
  61. this.stageInfo = canvas.getBoundingClientRect();
  62. },
  63. // 电脑设备事件
  64. mouseDown(ev) {
  65. ev = ev || event;
  66. ev.preventDefault();
  67. if (ev) {
  68. const obj = {
  69. x: ev.offsetX,
  70. y: ev.offsetY,
  71. };
  72. this.startX = obj.x;
  73. this.startY = obj.y;
  74. this.canvasTxt.beginPath();
  75. this.canvasTxt.moveTo(this.startX, this.startY);
  76. this.canvasTxt.lineTo(obj.x, obj.y);
  77. this.canvasTxt.stroke();
  78. this.canvasTxt.closePath();
  79. this.points.push(obj);
  80. this.isDown = true;
  81. }
  82. },
  83. // 移动设备事件
  84. touchStart(ev) {
  85. ev = ev || event;
  86. ev.preventDefault();
  87. if (ev.touches.length == 1) {
  88. const obj = {
  89. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  90. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  91. };
  92. this.startX = obj.x;
  93. this.startY = obj.y;
  94. this.canvasTxt.beginPath();
  95. this.canvasTxt.moveTo(this.startX, this.startY);
  96. this.canvasTxt.lineTo(obj.x, obj.y);
  97. this.canvasTxt.stroke();
  98. this.canvasTxt.closePath();
  99. this.points.push(obj);
  100. }
  101. },
  102. // 电脑设备事件
  103. mouseMove(ev) {
  104. ev = ev || event;
  105. ev.preventDefault();
  106. if (this.isDown) {
  107. const obj = {
  108. x: ev.offsetX,
  109. y: ev.offsetY,
  110. };
  111. this.moveY = obj.y;
  112. this.moveX = obj.x;
  113. this.canvasTxt.strokeStyle = this.strokeStyle;
  114. this.canvasTxt.lineWidth = this.lineWidth;
  115. this.canvasTxt.beginPath();
  116. this.canvasTxt.moveTo(this.startX, this.startY);
  117. this.canvasTxt.lineTo(obj.x, obj.y);
  118. this.canvasTxt.stroke();
  119. this.canvasTxt.closePath();
  120. this.startY = obj.y;
  121. this.startX = obj.x;
  122. this.points.push(obj);
  123. }
  124. },
  125. // 移动设备事件
  126. touchMove(ev) {
  127. ev = ev || event;
  128. ev.preventDefault();
  129. if (ev.touches.length == 1) {
  130. const obj = {
  131. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  132. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  133. };
  134. this.moveY = obj.y;
  135. this.moveX = obj.x;
  136. // 设置线条颜色
  137. this.canvasTxt.strokeStyle = this.strokeStyle;
  138. // 设置线条宽度
  139. this.canvasTxt.lineWidth = this.lineWidth;
  140. // 绘制开始路径
  141. this.canvasTxt.beginPath();
  142. // 定义线条开始坐标
  143. this.canvasTxt.moveTo(this.startX, this.startY);
  144. // 定义线条结束坐标
  145. this.canvasTxt.lineTo(obj.x, obj.y);
  146. // 绘制线条
  147. this.canvasTxt.stroke();
  148. this.canvasTxt.closePath();
  149. this.startY = obj.y;
  150. this.startX = obj.x;
  151. this.points.push(obj);
  152. }
  153. },
  154. // 电脑设备事件
  155. mouseUp(ev) {
  156. ev = ev || event;
  157. ev.preventDefault();
  158. if (ev) {
  159. const obj = {
  160. x: ev.offsetX,
  161. y: ev.offsetY,
  162. };
  163. this.canvasTxt.beginPath();
  164. this.canvasTxt.moveTo(this.startX, this.startY);
  165. this.canvasTxt.lineTo(obj.x, obj.y);
  166. this.canvasTxt.stroke();
  167. this.canvasTxt.closePath();
  168. this.points.push(obj);
  169. this.points.push({ x: -1, y: -1 });
  170. this.isDown = false;
  171. }
  172. },
  173. // 移动设备事件
  174. touchEnd(ev) {
  175. ev = ev || event;
  176. ev.preventDefault();
  177. if (ev.touches.length == 1) {
  178. const obj = {
  179. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  180. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  181. };
  182. this.canvasTxt.beginPath();
  183. this.canvasTxt.moveTo(this.startX, this.startY);
  184. this.canvasTxt.lineTo(obj.x, obj.y);
  185. this.canvasTxt.stroke();
  186. this.canvasTxt.closePath();
  187. this.points.push(obj);
  188. this.points.push({ x: -1, y: -1 });
  189. }
  190. },
  191. // 返回
  192. handleGoBack() {
  193. this.handleOverwrite();
  194. this.$emit('back');
  195. },
  196. // 重写
  197. handleOverwrite() {
  198. console.log('###');
  199. this.canvasTxt.clearRect(
  200. 0,
  201. 0,
  202. this.$refs.canvasF.width,
  203. this.$refs.canvasF.height
  204. );
  205. this.points = [];
  206. },
  207. // 提交
  208. handleSubmit() {
  209. if (this.points.length < 50) {
  210. return;
  211. }
  212. console.log('###', this.$refs.canvasF.toDataURL());
  213. // this.$emit('content', this.$refs.canvasF.toDataURL());
  214. },
  215. },
  216. };
  217. </script>
  218. <style lang="stylus" scoped>
  219. .signatureBox
  220. position fixed
  221. right 0
  222. bottom 0
  223. left 0
  224. background-color #fff
  225. .signature-title
  226. padding 10px 0
  227. background-color #f26226
  228. color #fff
  229. text-align center
  230. font-size 14px
  231. .canvasBox
  232. padding 10px
  233. height 220px
  234. canvas
  235. border 1px solid #f26226
  236. .signature-btn
  237. display flex
  238. justify-content center
  239. box-sizing border-box
  240. padding 0 10px 10px
  241. .btn
  242. display inline-block
  243. margin-top 10px
  244. margin-right 10px
  245. margin-bottom 10px
  246. padding 0
  247. width 40%
  248. height 30px
  249. border-color #f26226
  250. background-color #f26226
  251. line-height 30px
  252. &:last-child
  253. margin-right 0
  254. </style>

TS v0.0.1

  1. <template>
  2. <div class="signature__container">
  3. <div ref="canvasContainer" class="canvas__container">
  4. <div class="canvas__note">请在此区域内手写签名</div>
  5. <!--
  6. touchstart 移动端点击开始事件
  7. touchmove 移动端点击滑动事件
  8. touchend 移动端点击结束事件
  9. mousedown PC端鼠标点击事件
  10. mousemove PC端鼠标移动事件
  11. mouseup PC端鼠标抬起事件
  12. -->
  13. <canvas
  14. ref="canvasInstance"
  15. @touchstart="touchStart"
  16. @touchmove="touchMove"
  17. @touchend="touchEnd"
  18. @mousedown="mouseDown"
  19. @mousemove="mouseMove"
  20. @mouseup="mouseUp"
  21. />
  22. </div>
  23. <div class="signature__btns">
  24. <cube-button primary @click="handleSubmit">确认</cube-button>
  25. <cube-button outline @click="handleOverwrite">重签</cube-button>
  26. </div>
  27. </div>
  28. </template>
  29. <script lang="ts">
  30. import { Component, Vue } from 'vue-property-decorator';
  31. @Component
  32. export default class SignatureDemo extends Vue {
  33. points: Record<string, any>[] = [];
  34. canvasTxt: Record<string, any> | null = null;
  35. stageInfo: Record<string, any> = {};
  36. startX = 0;
  37. startY = 0;
  38. moveY = 0;
  39. moveX = 0;
  40. isDown = false;
  41. strokeStyle = '#000';
  42. lineWidth = 2;
  43. mounted() {
  44. this.initCanvas();
  45. }
  46. initCanvas() {
  47. const canvas: Record<string, any> = this.$refs.canvasInstance;
  48. // 获取画布的高度
  49. canvas.height = (this.$refs.canvasContainer as any).offsetHeight - 20;
  50. // 获取画布的宽度
  51. canvas.width = (this.$refs.canvasContainer as any).offsetWidth - 20;
  52. // 创建 context 对象
  53. this.canvasTxt = canvas.getContext('2d');
  54. this.stageInfo = canvas.getBoundingClientRect();
  55. }
  56. /** 电脑设备事件 */
  57. mouseDown(ev: any) {
  58. ev = ev || event;
  59. ev.preventDefault();
  60. if (ev) {
  61. const obj = {
  62. x: ev.offsetX,
  63. y: ev.offsetY,
  64. };
  65. this.startX = obj.x;
  66. this.startY = obj.y;
  67. this.canvasTxt?.beginPath();
  68. this.canvasTxt?.moveTo(this.startX, this.startY);
  69. this.canvasTxt?.lineTo(obj.x, obj.y);
  70. this.canvasTxt?.stroke();
  71. this.canvasTxt?.closePath();
  72. this.points.push(obj);
  73. this.isDown = true;
  74. }
  75. }
  76. /** 移动设备事件 */
  77. touchStart(ev: any) {
  78. ev = ev || event;
  79. ev.preventDefault();
  80. if (ev.touches.length == 1) {
  81. const obj = {
  82. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  83. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  84. };
  85. this.startX = obj.x;
  86. this.startY = obj.y;
  87. this.canvasTxt?.beginPath();
  88. this.canvasTxt?.moveTo(this.startX, this.startY);
  89. this.canvasTxt?.lineTo(obj.x, obj.y);
  90. this.canvasTxt?.stroke();
  91. this.canvasTxt?.closePath();
  92. this.points.push(obj);
  93. }
  94. }
  95. /** 电脑设备事件 */
  96. mouseMove(ev: any) {
  97. ev = ev || event;
  98. ev.preventDefault();
  99. if (this.isDown) {
  100. const obj = {
  101. x: ev.offsetX,
  102. y: ev.offsetY,
  103. };
  104. this.moveY = obj.y;
  105. this.moveX = obj.x;
  106. if (this.canvasTxt) {
  107. this.canvasTxt.strokeStyle = this.strokeStyle;
  108. this.canvasTxt.lineWidth = this.lineWidth;
  109. }
  110. this.canvasTxt?.beginPath();
  111. this.canvasTxt?.moveTo(this.startX, this.startY);
  112. this.canvasTxt?.lineTo(obj.x, obj.y);
  113. this.canvasTxt?.stroke();
  114. this.canvasTxt?.closePath();
  115. this.startY = obj.y;
  116. this.startX = obj.x;
  117. this.points.push(obj);
  118. }
  119. }
  120. /** 移动设备事件 */
  121. touchMove(ev: any) {
  122. ev = ev || event;
  123. ev.preventDefault();
  124. if (ev.touches.length == 1) {
  125. const obj = {
  126. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  127. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  128. };
  129. this.moveY = obj.y;
  130. this.moveX = obj.x;
  131. if (this.canvasTxt) {
  132. // 设置线条颜色
  133. this.canvasTxt.strokeStyle = this.strokeStyle;
  134. // 设置线条宽度
  135. this.canvasTxt.lineWidth = this.lineWidth;
  136. }
  137. // 绘制开始路径
  138. this.canvasTxt?.beginPath();
  139. // 定义线条开始坐标
  140. this.canvasTxt?.moveTo(this.startX, this.startY);
  141. // 定义线条结束坐标
  142. this.canvasTxt?.lineTo(obj.x, obj.y);
  143. // 绘制线条
  144. this.canvasTxt?.stroke();
  145. this.canvasTxt?.closePath();
  146. this.startY = obj.y;
  147. this.startX = obj.x;
  148. this.points.push(obj);
  149. }
  150. }
  151. /** 电脑设备事件 */
  152. mouseUp(ev: any) {
  153. ev = ev || event;
  154. ev.preventDefault();
  155. if (ev) {
  156. const obj = {
  157. x: ev.offsetX,
  158. y: ev.offsetY,
  159. };
  160. this.canvasTxt?.beginPath();
  161. this.canvasTxt?.moveTo(this.startX, this.startY);
  162. this.canvasTxt?.lineTo(obj.x, obj.y);
  163. this.canvasTxt?.stroke();
  164. this.canvasTxt?.closePath();
  165. this.points.push(obj);
  166. this.points.push({ x: -1, y: -1 });
  167. this.isDown = false;
  168. }
  169. }
  170. /** 移动设备事件 */
  171. touchEnd(ev: any) {
  172. ev = ev || event;
  173. ev.preventDefault();
  174. if (ev.touches.length == 1) {
  175. const obj = {
  176. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  177. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  178. };
  179. this.canvasTxt?.beginPath();
  180. this.canvasTxt?.moveTo(this.startX, this.startY);
  181. this.canvasTxt?.lineTo(obj.x, obj.y);
  182. this.canvasTxt?.stroke();
  183. this.canvasTxt?.closePath();
  184. this.points.push(obj);
  185. this.points.push({ x: -1, y: -1 });
  186. }
  187. }
  188. /** 重写 */
  189. handleOverwrite() {
  190. this.canvasTxt?.clearRect(
  191. 0,
  192. 0,
  193. (this.$refs.canvasInstance as any).width,
  194. (this.$refs.canvasInstance as any).height
  195. );
  196. this.points = [];
  197. }
  198. /** 提交 */
  199. handleSubmit() {
  200. if (this.points.length < 50) {
  201. return;
  202. }
  203. console.log('###', (this.$refs.canvasInstance as any).toDataURL());
  204. // this.$emit('content', this.$refs.canvasInstance.toDataURL());
  205. }
  206. }
  207. </script>
  208. <style lang="stylus" scoped>
  209. @import '~common/stylus/variable'
  210. @import '~common/stylus/mixin'
  211. .signature__container
  212. width 100vw
  213. height 100vh
  214. background #F9FBFF
  215. .canvas__container
  216. padding 10px
  217. height calc(100vh - 100px)
  218. .canvas__note
  219. position fixed
  220. top 300px
  221. right 60px
  222. left 60px
  223. z-index -1
  224. color #B9BFC8
  225. text-align center
  226. font-size 12px
  227. line-height 17px
  228. canvas
  229. border 1px solid #f26226
  230. .signature__btns
  231. flex-row()
  232. padding 0 10px
  233. </style>

TS v0.0.2(横屏)

  1. <template>
  2. <div class="signature__container">
  3. <!-- 按钮区域 -->
  4. <div class="signature__btns">
  5. <cube-button
  6. class="signature__btn resign"
  7. outline
  8. @click="handleOverwrite"
  9. >
  10. 清除手写板
  11. </cube-button>
  12. <cube-button class="signature__btn confirm" primary @click="handleSubmit">
  13. 确认签名
  14. </cube-button>
  15. <div v-if="false" class="canvas__note">请在此区域内手写签名</div>
  16. </div>
  17. <!-- 签字板区域 -->
  18. <div ref="canvasContainer" class="canvas__container">
  19. <!--
  20. touchstart 移动端点击开始事件
  21. touchmove 移动端点击滑动事件
  22. touchend 移动端点击结束事件
  23. mousedown PC端鼠标点击事件
  24. mousemove PC端鼠标移动事件
  25. mouseup PC端鼠标抬起事件
  26. -->
  27. <canvas
  28. ref="canvasInstance"
  29. @touchstart="touchStart"
  30. @touchmove="touchMove"
  31. @touchend="touchEnd"
  32. @mousedown="mouseDown"
  33. @mousemove="mouseMove"
  34. @mouseup="mouseUp"
  35. />
  36. </div>
  37. </div>
  38. </template>
  39. <script lang="ts">
  40. import { Component, Vue } from 'vue-property-decorator';
  41. @Component
  42. export default class SignatureDemo extends Vue {
  43. points: Record<string, any>[] = [];
  44. canvasTxt: Record<string, any> | null = null;
  45. stageInfo: Record<string, any> = {};
  46. startX = 0;
  47. startY = 0;
  48. moveY = 0;
  49. moveX = 0;
  50. isDown = false;
  51. strokeStyle = '#000';
  52. lineWidth = 2;
  53. mounted() {
  54. this.initCanvas();
  55. }
  56. initCanvas() {
  57. const canvas: Record<string, any> = this.$refs.canvasInstance;
  58. // 获取画布的高度
  59. canvas.height = (this.$refs.canvasContainer as any).offsetHeight;
  60. // 获取画布的宽度
  61. canvas.width = (this.$refs.canvasContainer as any).offsetWidth;
  62. // 创建 context 对象
  63. this.canvasTxt = canvas.getContext('2d');
  64. this.stageInfo = canvas.getBoundingClientRect();
  65. }
  66. /** 电脑设备事件 */
  67. mouseDown(ev: any) {
  68. ev = ev || event;
  69. ev.preventDefault();
  70. if (ev) {
  71. const obj = {
  72. x: ev.offsetX,
  73. y: ev.offsetY,
  74. };
  75. this.startX = obj.x;
  76. this.startY = obj.y;
  77. this.canvasTxt?.beginPath();
  78. this.canvasTxt?.moveTo(this.startX, this.startY);
  79. this.canvasTxt?.lineTo(obj.x, obj.y);
  80. this.canvasTxt?.stroke();
  81. this.canvasTxt?.closePath();
  82. this.points.push(obj);
  83. this.isDown = true;
  84. }
  85. }
  86. /** 移动设备事件 */
  87. touchStart(ev: any) {
  88. ev = ev || event;
  89. ev.preventDefault();
  90. if (ev.touches.length == 1) {
  91. const obj = {
  92. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  93. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  94. };
  95. this.startX = obj.x;
  96. this.startY = obj.y;
  97. this.canvasTxt?.beginPath();
  98. this.canvasTxt?.moveTo(this.startX, this.startY);
  99. this.canvasTxt?.lineTo(obj.x, obj.y);
  100. this.canvasTxt?.stroke();
  101. this.canvasTxt?.closePath();
  102. this.points.push(obj);
  103. }
  104. }
  105. /** 电脑设备事件 */
  106. mouseMove(ev: any) {
  107. ev = ev || event;
  108. ev.preventDefault();
  109. if (this.isDown) {
  110. const obj = {
  111. x: ev.offsetX,
  112. y: ev.offsetY,
  113. };
  114. this.moveY = obj.y;
  115. this.moveX = obj.x;
  116. if (this.canvasTxt) {
  117. this.canvasTxt.strokeStyle = this.strokeStyle;
  118. this.canvasTxt.lineWidth = this.lineWidth;
  119. }
  120. this.canvasTxt?.beginPath();
  121. this.canvasTxt?.moveTo(this.startX, this.startY);
  122. this.canvasTxt?.lineTo(obj.x, obj.y);
  123. this.canvasTxt?.stroke();
  124. this.canvasTxt?.closePath();
  125. this.startY = obj.y;
  126. this.startX = obj.x;
  127. this.points.push(obj);
  128. }
  129. }
  130. /** 移动设备事件 */
  131. touchMove(ev: any) {
  132. ev = ev || event;
  133. ev.preventDefault();
  134. if (ev.touches.length == 1) {
  135. const obj = {
  136. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  137. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  138. };
  139. this.moveY = obj.y;
  140. this.moveX = obj.x;
  141. if (this.canvasTxt) {
  142. // 设置线条颜色
  143. this.canvasTxt.strokeStyle = this.strokeStyle;
  144. // 设置线条宽度
  145. this.canvasTxt.lineWidth = this.lineWidth;
  146. }
  147. // 绘制开始路径
  148. this.canvasTxt?.beginPath();
  149. // 定义线条开始坐标
  150. this.canvasTxt?.moveTo(this.startX, this.startY);
  151. // 定义线条结束坐标
  152. this.canvasTxt?.lineTo(obj.x, obj.y);
  153. // 绘制线条
  154. this.canvasTxt?.stroke();
  155. this.canvasTxt?.closePath();
  156. this.startY = obj.y;
  157. this.startX = obj.x;
  158. this.points.push(obj);
  159. }
  160. }
  161. /** 电脑设备事件 */
  162. mouseUp(ev: any) {
  163. ev = ev || event;
  164. ev.preventDefault();
  165. if (ev) {
  166. const obj = {
  167. x: ev.offsetX,
  168. y: ev.offsetY,
  169. };
  170. this.canvasTxt?.beginPath();
  171. this.canvasTxt?.moveTo(this.startX, this.startY);
  172. this.canvasTxt?.lineTo(obj.x, obj.y);
  173. this.canvasTxt?.stroke();
  174. this.canvasTxt?.closePath();
  175. this.points.push(obj);
  176. this.points.push({ x: -1, y: -1 });
  177. this.isDown = false;
  178. }
  179. }
  180. /** 移动设备事件 */
  181. touchEnd(ev: any) {
  182. ev = ev || event;
  183. ev.preventDefault();
  184. if (ev.touches.length == 1) {
  185. const obj = {
  186. x: ev.targetTouches[0].clientX - this.stageInfo.left,
  187. y: ev.targetTouches[0].clientY - this.stageInfo.top,
  188. };
  189. this.canvasTxt?.beginPath();
  190. this.canvasTxt?.moveTo(this.startX, this.startY);
  191. this.canvasTxt?.lineTo(obj.x, obj.y);
  192. this.canvasTxt?.stroke();
  193. this.canvasTxt?.closePath();
  194. this.points.push(obj);
  195. this.points.push({ x: -1, y: -1 });
  196. }
  197. }
  198. /** 重写 */
  199. handleOverwrite() {
  200. this.canvasTxt?.clearRect(
  201. 0,
  202. 0,
  203. (this.$refs.canvasInstance as any).width,
  204. (this.$refs.canvasInstance as any).height
  205. );
  206. this.points = [];
  207. }
  208. /** 提交 */
  209. handleSubmit() {
  210. if (this.points.length < 50) {
  211. return;
  212. }
  213. console.log('###', (this.$refs.canvasInstance as any).toDataURL());
  214. // this.$emit('content', this.$refs.canvasInstance.toDataURL());
  215. }
  216. }
  217. </script>
  218. <style lang="stylus" scoped>
  219. @import '~common/stylus/variable'
  220. @import '~common/stylus/mixin'
  221. .signature__container
  222. position relative
  223. overflow auto
  224. box-sizing border-box
  225. padding 33px 24px 33px 60.5px
  226. width 100vw
  227. height 100vh
  228. .signature__btns
  229. flex-row()
  230. position absolute
  231. top -50.5px
  232. right calc(100vw - 100vh) // 100vw 屏幕宽度,剩下的宽度是 100vh - 100vw
  233. left 0
  234. justify-content space-between
  235. padding 0 33px
  236. transform rotate(90deg)
  237. transform-origin bottom left
  238. .signature__btn
  239. width 150px
  240. height 50.5px
  241. border-radius 10px
  242. background #FFFFFF
  243. &.resign
  244. background #FFFFFF
  245. color #626773
  246. &:after
  247. border-radius 10PX
  248. &.confirm
  249. background #10CCA2
  250. .canvas__container
  251. height 100%
  252. .canvas__note
  253. color #B9BFC8
  254. text-align center
  255. font-size 12px
  256. canvas
  257. border 1px dashed #10CCA2
  258. </style>

TS v0.0.3(简化)

  1. <template>
  2. <div class="signature__container">
  3. <!-- 签字板区域 -->
  4. <div ref="canvasContainer" class="canvas__container">
  5. <div v-if="isShowNote" class="canvas__note" @touchstart="onHideNote">
  6. 请在此区域内手写签名
  7. </div>
  8. <!--
  9. touchstart 移动端点击开始事件
  10. touchmove 移动端点击滑动事件
  11. touchend 移动端点击结束事件
  12. -->
  13. <canvas
  14. ref="canvasRef"
  15. class="canvas"
  16. @touchstart="onTouchStart"
  17. @touchmove="onTouchMove"
  18. @touchend="onTouchEnd"
  19. />
  20. </div>
  21. <!-- 按钮区域 -->
  22. <div class="signature__btns">
  23. <cube-button class="signature__btn resign" outline @click="onRewrite">
  24. 清除手写板
  25. </cube-button>
  26. <cube-button class="signature__btn confirm" primary @click="onSubmit">
  27. 确认签名
  28. </cube-button>
  29. </div>
  30. </div>
  31. </template>
  32. <script lang="ts">
  33. import { Component, Vue, Emit } from 'vue-property-decorator';
  34. /**
  35. * ***
  36. * 为什么该组件不使用整体旋转的方式实现的几点思考:
  37. * 1. 整体旋转最根本的原先的绘制canvas的x、y坐标必须更改,代码修改难度大。
  38. * ***
  39. */
  40. /** 字体颜色 */
  41. const STROKE_STYLE = '#202020';
  42. /** 字体宽度 */
  43. const LINE_WIDTH = 2;
  44. @Component
  45. export default class SignatureDemo extends Vue {
  46. /** 是否展示提示信息 */
  47. isShowNote = true;
  48. /** canvas实例对象 */
  49. canvasInstance: Record<string, any> | null = null;
  50. /** 画布信息 */
  51. stageInfo: Record<string, any> = {};
  52. /** 绘制点位x轴坐标 */
  53. startX = 0;
  54. /** 绘制点位y轴坐标 */
  55. startY = 0;
  56. mounted() {
  57. this.initial();
  58. }
  59. /** 初始化 */
  60. initial() {
  61. const canvas: Record<string, any> = this.$refs.canvasRef;
  62. // 设置画布的高度
  63. canvas.height = (this.$refs.canvasContainer as any).offsetHeight;
  64. // 设置画布的宽度
  65. canvas.width = (this.$refs.canvasContainer as any).offsetWidth;
  66. // 创建 context 对象
  67. this.canvasInstance = canvas.getContext('2d');
  68. // 获取画布信息
  69. this.stageInfo = canvas.getBoundingClientRect();
  70. }
  71. /** 隐藏提示信息 */
  72. onHideNote() {
  73. this.isShowNote = false;
  74. }
  75. /** 【移动设备】点击开始事件 */
  76. onTouchStart(event: any) {
  77. this.isShowNote = false;
  78. event.preventDefault();
  79. if (event.touches.length == 1) {
  80. const obj = {
  81. x: event.targetTouches[0].clientX - this.stageInfo.left,
  82. y: event.targetTouches[0].clientY - this.stageInfo.top,
  83. };
  84. this.startX = obj.x;
  85. this.startY = obj.y;
  86. this.canvasInstance?.beginPath();
  87. this.canvasInstance?.moveTo(this.startX, this.startY);
  88. this.canvasInstance?.lineTo(obj.x, obj.y);
  89. this.canvasInstance?.stroke();
  90. this.canvasInstance?.closePath();
  91. }
  92. }
  93. /** 【移动设备】点击移动事件 */
  94. onTouchMove(event: any) {
  95. event.preventDefault();
  96. if (event.touches.length == 1) {
  97. const obj = {
  98. x: event.targetTouches[0].clientX - this.stageInfo.left,
  99. y: event.targetTouches[0].clientY - this.stageInfo.top,
  100. };
  101. if (this.canvasInstance) {
  102. // 设置线条颜色
  103. this.canvasInstance.strokeStyle = STROKE_STYLE;
  104. // 设置线条宽度
  105. this.canvasInstance.lineWidth = LINE_WIDTH;
  106. }
  107. // 绘制开始路径
  108. this.canvasInstance?.beginPath();
  109. // 定义线条开始坐标
  110. this.canvasInstance?.moveTo(this.startX, this.startY);
  111. // 定义线条结束坐标
  112. this.canvasInstance?.lineTo(obj.x, obj.y);
  113. // 绘制线条
  114. this.canvasInstance?.stroke();
  115. // 停止绘制
  116. this.canvasInstance?.closePath();
  117. this.startY = obj.y;
  118. this.startX = obj.x;
  119. }
  120. }
  121. /** 【移动设备】点击结束事件 */
  122. onTouchEnd(event: any) {
  123. event.preventDefault();
  124. if (event.touches.length == 1) {
  125. const obj = {
  126. x: event.targetTouches[0].clientX - this.stageInfo.left,
  127. y: event.targetTouches[0].clientY - this.stageInfo.top,
  128. };
  129. this.canvasInstance?.beginPath();
  130. this.canvasInstance?.moveTo(this.startX, this.startY);
  131. this.canvasInstance?.lineTo(obj.x, obj.y);
  132. this.canvasInstance?.stroke();
  133. this.canvasInstance?.closePath();
  134. }
  135. }
  136. /** 重置 */
  137. onRewrite() {
  138. this.canvasInstance?.clearRect(
  139. 0,
  140. 0,
  141. (this.$refs.canvasRef as any).width,
  142. (this.$refs.canvasRef as any).height
  143. );
  144. // 隐藏提示信息
  145. this.isShowNote = true;
  146. }
  147. /** 提交 */
  148. @Emit('submit')
  149. onSubmit() {
  150. return (this.$refs.canvasRef as any).toDataURL();
  151. }
  152. }
  153. </script>
  154. <style lang="stylus" scoped>
  155. @import '~common/stylus/variable'
  156. @import '~common/stylus/mixin'
  157. .signature__container
  158. position relative
  159. box-sizing border-box
  160. padding 33px 24px 33px 60.5px
  161. width 100vw
  162. height 100vh
  163. background #F9FBFF
  164. .canvas__container
  165. height 100%
  166. .canvas__note
  167. position absolute
  168. top -22px // 补偿字体高度
  169. right calc(50vw - 100vh) // 100vw 屏幕宽度,剩下的宽度是 100vw - 100vh,因为超出屏幕所以要取反
  170. left 50vw
  171. z-index 1
  172. color #B9BFC8
  173. text-align center
  174. font-size 22px
  175. line-height 22px
  176. transform rotate(90deg)
  177. transform-origin bottom left
  178. .canvas
  179. border 1px dashed #10CCA2
  180. border-radius 6px
  181. background #FFFFFF
  182. .signature__btns
  183. flex-row()
  184. position absolute
  185. top -50.5px
  186. right calc(100vw - 100vh) // 100vw 屏幕宽度,剩下的宽度是 100vw - 100vh,因为超出屏幕所以要取反
  187. left 0
  188. justify-content space-between
  189. padding 0 33px
  190. transform rotate(90deg)
  191. transform-origin bottom left
  192. .signature__btn
  193. width 150px
  194. height 50.5px
  195. border-radius 10px
  196. background #FFFFFF
  197. &.resign
  198. background #FFFFFF
  199. color #626773
  200. &:after
  201. border-radius 10PX
  202. &.confirm
  203. background #10CCA2
  204. </style>