index.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. import api from '../api/api'
  2. Page({
  3. data: {
  4. attachmentids: [],
  5. canvasName: 'signCanvas',
  6. ctx: '', // 上下文实例
  7. canvas: '', // 画布实例
  8. canvasWidth: 0,
  9. canvasHeight: 0,
  10. transparent: 1, // 透明度
  11. selectColor: 'black',
  12. lineColor: '#1A1A1A', // 颜色
  13. lineSize: 1.5, // 笔记倍数
  14. lineMin: 0.5, // 最小笔画半径
  15. lineMax: 4, // 最大笔画半径
  16. pressure: 1, // 默认压力
  17. smoothness: 60, //顺滑度,用60的距离来计算速度
  18. currentPoint: {},
  19. currentLine: [], // 当前线条
  20. firstTouch: true, // 第一次触发
  21. radius: 1, //画圆的半径
  22. cutArea: {
  23. top: 0,
  24. right: 0,
  25. bottom: 0,
  26. left: 0
  27. }, //裁剪区域
  28. bethelPoint: [], //保存所有线条 生成的贝塞尔点;
  29. lastPoint: 0,
  30. chirography: [], //笔迹
  31. currentChirography: {}, //当前笔迹
  32. linePrack: [], //划线轨迹 , 生成线条的实际点
  33. isDraw: false, // 判断用户是否绘制
  34. upPageNumber: 0,
  35. offCanvas: '',
  36. offCtx: '',
  37. dpr: ''
  38. },
  39. /**
  40. * 生命周期函数--监听页面加载
  41. */
  42. onLoad: function (options) {
  43. getApp().globalData.Language.getLanguagePackage(this, 'E-订单');
  44. this.setData({
  45. id: options.id
  46. })
  47. this.data.dpr = wx.getSystemInfoSync().pixelRatio
  48. const query = wx.createSelectorQuery()
  49. query
  50. .select('.handCenter')
  51. .boundingClientRect(rect => {
  52. this.data.canvasWidth = rect.width * this.data.dpr
  53. this.data.canvasHeight = rect.height * this.data.dpr
  54. this.setData({
  55. canvasWidth:this.data.canvasWidth,
  56. canvasHeight:this.data.canvasHeight
  57. })
  58. })
  59. .exec()
  60. const query1 = wx.createSelectorQuery()
  61. query1
  62. .select('#signCanvas')
  63. .fields({
  64. node: true,
  65. size: true
  66. })
  67. .exec(res => {
  68. const canvas = res[0].node
  69. this.data.ctx = canvas.getContext('2d')
  70. canvas.width = this.data.canvasWidth
  71. canvas.height = this.data.canvasHeight
  72. /* 将canvas背景设置为 白底,不设置 导出的canvas的背景为透明 */
  73. // console.log(this, 'hahah');
  74. this.data.canvas = canvas
  75. this.data.ctx.scale(this.data.dpr, this.data.dpr)
  76. this.setCanvasBg('#fff')
  77. })
  78. const query2 = wx.createSelectorQuery()
  79. query2
  80. .select('#offCanvas')
  81. .fields({
  82. node: true,
  83. size: true
  84. })
  85. .exec(res => {
  86. const canvas = res[0].node
  87. const ctx = canvas.getContext('2d')
  88. canvas.width = this.data.canvasHeight
  89. canvas.height = this.data.canvasWidth
  90. this.data.offCanvas = canvas
  91. this.data.offCtx = ctx
  92. this.data.offCtx.scale(this.data.dpr, this.data.dpr)
  93. })
  94. },
  95. // 重写事件(初始化画布)
  96. retDraw() {
  97. this.data.ctx.clearRect(0, 0, 700, 730)
  98. //设置canvas背景
  99. this.setCanvasBg('#fff')
  100. this.data.isDraw = false
  101. },
  102. // 完成事件
  103. subCanvas() {
  104. // 调用预览
  105. console.log('========sub')
  106. this.previewCanvasImg()
  107. console.log('========subOver')
  108. },
  109. //画两点之间的线条;参数为:line,会绘制最近的开始的两个点;
  110. pointToLine(line) {
  111. this.calcBethelLine(line)
  112. return
  113. },
  114. // 笔迹开始
  115. uploadScaleStart(e) {
  116. if (e.type != 'touchstart') return false
  117. let ctx = this.data.ctx
  118. ctx.fillStyle = this.data.lineColor // 初始线条设置颜色
  119. ctx.globalAlpha = this.data.transparent // 设置半透明
  120. let currentPoint = {
  121. x: e.touches[0].x,
  122. y: e.touches[0].y
  123. }
  124. let currentLine = this.data.currentLine
  125. currentLine.unshift({
  126. time: new Date().getTime(),
  127. dis: 0,
  128. x: currentPoint.x,
  129. y: currentPoint.y
  130. })
  131. this.setData({
  132. currentPoint
  133. // currentLine
  134. })
  135. if (this.data.firstTouch) {
  136. this.setData({
  137. cutArea: {
  138. top: currentPoint.y,
  139. right: currentPoint.x,
  140. bottom: currentPoint.y,
  141. left: currentPoint.x
  142. },
  143. firstTouch: false
  144. })
  145. }
  146. this.pointToLine(currentLine)
  147. },
  148. // 笔迹移动
  149. uploadScaleMove(e) {
  150. if (e.type != 'touchmove') return false
  151. if (e.cancelable) {
  152. // 判断默认行为是否已经被禁用
  153. if (!e.defaultPrevented) {
  154. e.preventDefault()
  155. }
  156. }
  157. let point = {
  158. x: e.touches[0].x,
  159. y: e.touches[0].y
  160. }
  161. //测试裁剪
  162. if (point.y < this.data.cutArea.top) {
  163. this.data.cutArea.top = point.y
  164. }
  165. if (point.y < 0) this.data.cutArea.top = 0
  166. if (point.x > this.data.cutArea.right) {
  167. this.data.cutArea.right = point.x
  168. }
  169. if (this.data.canvasWidth - point.x <= 0) {
  170. this.data.cutArea.right = this.data.canvasWidth
  171. }
  172. if (point.y > this.data.cutArea.bottom) {
  173. this.data.cutArea.bottom = point.y
  174. }
  175. if (this.data.canvasHeight - point.y <= 0) {
  176. this.data.cutArea.bottom = this.data.canvasHeight
  177. }
  178. if (point.x < this.data.cutArea.left) {
  179. this.data.cutArea.left = point.x
  180. }
  181. if (point.x < 0) this.data.cutArea.left = 0
  182. this.setData({
  183. lastPoint: this.data.currentPoint,
  184. currentPoint: point
  185. })
  186. let currentLine = this.data.currentLine
  187. currentLine.unshift({
  188. time: new Date().getTime(),
  189. dis: this.distance(this.data.currentPoint, this.data.lastPoint),
  190. x: point.x,
  191. y: point.y
  192. })
  193. // this.setData({
  194. // currentLine
  195. // })
  196. this.pointToLine(currentLine)
  197. },
  198. // 笔迹结束
  199. uploadScaleEnd(e) {
  200. if (e.type != 'touchend') return 0
  201. let point = {
  202. x: e.changedTouches[0].x,
  203. y: e.changedTouches[0].y
  204. }
  205. this.setData({
  206. lastPoint: this.data.currentPoint,
  207. currentPoint: point
  208. })
  209. let currentLine = this.data.currentLine
  210. currentLine.unshift({
  211. time: new Date().getTime(),
  212. dis: this.distance(this.data.currentPoint, this.data.lastPoint),
  213. x: point.x,
  214. y: point.y
  215. })
  216. // this.setData({
  217. // currentLine
  218. // })
  219. if (currentLine.length > 2) {
  220. var info =
  221. (currentLine[0].time - currentLine[currentLine.length - 1].time) / currentLine.length
  222. //$("#info").text(info.toFixed(2));
  223. }
  224. //一笔结束,保存笔迹的坐标点,清空,当前笔迹
  225. //增加判断是否在手写区域;
  226. this.pointToLine(currentLine)
  227. var currentChirography = {
  228. lineSize: this.data.lineSize,
  229. lineColor: this.data.lineColor
  230. }
  231. var chirography = this.data.chirography
  232. chirography.unshift(currentChirography)
  233. this.setData({
  234. chirography
  235. })
  236. var linePrack = this.data.linePrack
  237. linePrack.unshift(this.data.currentLine)
  238. this.setData({
  239. linePrack,
  240. currentLine: []
  241. })
  242. this.data.isDraw = true
  243. },
  244. //计算插值的方式;
  245. calcBethelLine(line) {
  246. if (line.length <= 1) {
  247. line[0].r = this.data.radius
  248. return
  249. }
  250. let x0,
  251. x1,
  252. x2,
  253. y0,
  254. y1,
  255. y2,
  256. r0,
  257. r1,
  258. r2,
  259. len,
  260. lastRadius,
  261. dis = 0,
  262. time = 0,
  263. curveValue = 0.5
  264. if (line.length <= 2) {
  265. x0 = line[1].x
  266. y0 = line[1].y
  267. x2 = line[1].x + (line[0].x - line[1].x) * curveValue
  268. y2 = line[1].y + (line[0].y - line[1].y) * curveValue
  269. //x2 = line[1].x;
  270. //y2 = line[1].y;
  271. x1 = x0 + (x2 - x0) * curveValue
  272. y1 = y0 + (y2 - y0) * curveValue
  273. } else {
  274. x0 = line[2].x + (line[1].x - line[2].x) * curveValue
  275. y0 = line[2].y + (line[1].y - line[2].y) * curveValue
  276. x1 = line[1].x
  277. y1 = line[1].y
  278. x2 = x1 + (line[0].x - x1) * curveValue
  279. y2 = y1 + (line[0].y - y1) * curveValue
  280. }
  281. //从计算公式看,三个点分别是(x0,y0),(x1,y1),(x2,y2) ;(x1,y1)这个是控制点,控制点不会落在曲线上;实际上,这个点还会手写获取的实际点,却落在曲线上
  282. len = this.distance({
  283. x: x2,
  284. y: y2
  285. }, {
  286. x: x0,
  287. y: y0
  288. })
  289. lastRadius = this.data.radius
  290. for (let n = 0; n < line.length - 1; n++) {
  291. dis += line[n].dis
  292. time += line[n].time - line[n + 1].time
  293. if (dis > this.data.smoothness) break
  294. }
  295. this.setData({
  296. radius: Math.min((time / len) * this.data.pressure + this.data.lineMin, this.data.lineMax) *
  297. this.data.lineSize
  298. })
  299. line[0].r = this.data.radius
  300. //计算笔迹半径;
  301. if (line.length <= 2) {
  302. r0 = (lastRadius + this.data.radius) / 2
  303. r1 = r0
  304. r2 = r1
  305. //return;
  306. } else {
  307. r0 = (line[2].r + line[1].r) / 2
  308. r1 = line[1].r
  309. r2 = (line[1].r + line[0].r) / 2
  310. }
  311. let n = 5
  312. let point = []
  313. for (let i = 0; i < n; i++) {
  314. let t = i / (n - 1)
  315. let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2
  316. let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2
  317. let r = lastRadius + ((this.data.radius - lastRadius) / n) * i
  318. point.push({
  319. x: x,
  320. y: y,
  321. r: r
  322. })
  323. if (point.length == 3) {
  324. let a = this.ctaCalc(
  325. point[0].x,
  326. point[0].y,
  327. point[0].r,
  328. point[1].x,
  329. point[1].y,
  330. point[1].r,
  331. point[2].x,
  332. point[2].y,
  333. point[2].r
  334. )
  335. a[0].color = this.data.lineColor
  336. // let bethelPoint = this.data.bethelPoint;
  337. // console.log(a)
  338. // console.log(this.data.bethelPoint)
  339. // bethelPoint = bethelPoint.push(a);
  340. this.bethelDraw(a, 1)
  341. point = [{
  342. x: x,
  343. y: y,
  344. r: r
  345. }]
  346. }
  347. }
  348. this.setData({
  349. currentLine: line
  350. })
  351. },
  352. //求两点之间距离
  353. distance(a, b) {
  354. let x = b.x - a.x
  355. let y = b.y - a.y
  356. return Math.sqrt(x * x + y * y)
  357. },
  358. ctaCalc(x0, y0, r0, x1, y1, r1, x2, y2, r2) {
  359. let a = [],
  360. vx01,
  361. vy01,
  362. norm,
  363. n_x0,
  364. n_y0,
  365. vx21,
  366. vy21,
  367. n_x2,
  368. n_y2
  369. vx01 = x1 - x0
  370. vy01 = y1 - y0
  371. norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2
  372. vx01 = (vx01 / norm) * r0
  373. vy01 = (vy01 / norm) * r0
  374. n_x0 = vy01
  375. n_y0 = -vx01
  376. vx21 = x1 - x2
  377. vy21 = y1 - y2
  378. norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2
  379. vx21 = (vx21 / norm) * r2
  380. vy21 = (vy21 / norm) * r2
  381. n_x2 = -vy21
  382. n_y2 = vx21
  383. a.push({
  384. mx: x0 + n_x0,
  385. my: y0 + n_y0,
  386. color: '#1A1A1A'
  387. })
  388. a.push({
  389. c1x: x1 + n_x0,
  390. c1y: y1 + n_y0,
  391. c2x: x1 + n_x2,
  392. c2y: y1 + n_y2,
  393. ex: x2 + n_x2,
  394. ey: y2 + n_y2
  395. })
  396. a.push({
  397. c1x: x2 + n_x2 - vx21,
  398. c1y: y2 + n_y2 - vy21,
  399. c2x: x2 - n_x2 - vx21,
  400. c2y: y2 - n_y2 - vy21,
  401. ex: x2 - n_x2,
  402. ey: y2 - n_y2
  403. })
  404. a.push({
  405. c1x: x1 - n_x2,
  406. c1y: y1 - n_y2,
  407. c2x: x1 - n_x0,
  408. c2y: y1 - n_y0,
  409. ex: x0 - n_x0,
  410. ey: y0 - n_y0
  411. })
  412. a.push({
  413. c1x: x0 - n_x0 - vx01,
  414. c1y: y0 - n_y0 - vy01,
  415. c2x: x0 + n_x0 - vx01,
  416. c2y: y0 + n_y0 - vy01,
  417. ex: x0 + n_x0,
  418. ey: y0 + n_y0
  419. })
  420. a[0].mx = a[0].mx.toFixed(1)
  421. a[0].mx = parseFloat(a[0].mx)
  422. a[0].my = a[0].my.toFixed(1)
  423. a[0].my = parseFloat(a[0].my)
  424. for (let i = 1; i < a.length; i++) {
  425. a[i].c1x = a[i].c1x.toFixed(1)
  426. a[i].c1x = parseFloat(a[i].c1x)
  427. a[i].c1y = a[i].c1y.toFixed(1)
  428. a[i].c1y = parseFloat(a[i].c1y)
  429. a[i].c2x = a[i].c2x.toFixed(1)
  430. a[i].c2x = parseFloat(a[i].c2x)
  431. a[i].c2y = a[i].c2y.toFixed(1)
  432. a[i].c2y = parseFloat(a[i].c2y)
  433. a[i].ex = a[i].ex.toFixed(1)
  434. a[i].ex = parseFloat(a[i].ex)
  435. a[i].ey = a[i].ey.toFixed(1)
  436. a[i].ey = parseFloat(a[i].ey)
  437. }
  438. return a
  439. },
  440. bethelDraw(point, is_fill, color) {
  441. let ctx = this.data.ctx
  442. ctx.beginPath()
  443. ctx.moveTo(point[0].mx, point[0].my)
  444. if (undefined != color) {
  445. ctx.fillStyle = color
  446. ctx.strokeStyle = color
  447. } else {
  448. ctx.fillStyle = point[0].color
  449. ctx.strokeStyle = point[0].color
  450. }
  451. for (let i = 1; i < point.length; i++) {
  452. ctx.bezierCurveTo(
  453. point[i].c1x,
  454. point[i].c1y,
  455. point[i].c2x,
  456. point[i].c2y,
  457. point[i].ex,
  458. point[i].ey
  459. )
  460. }
  461. ctx.stroke()
  462. if (undefined != is_fill) {
  463. ctx.fill() //填充图形 ( 后绘制的图形会覆盖前面的图形, 绘制时注意先后顺序 )
  464. }
  465. },
  466. //设置canvas背景色 不设置 导出的canvas的背景为透明
  467. //@params:字符串 color
  468. setCanvasBg(color) {
  469. console.log('开始设置背景色')
  470. /* 将canvas背景设置为 白底,不设置 导出的canvas的背景为透明 */
  471. //rect() 参数说明 矩形路径左上角的横坐标,左上角的纵坐标, 矩形路径的宽度, 矩形路径的高度
  472. //这里是 canvasHeight - 4 是因为下边盖住边框了,所以手动减了写
  473. this.data.ctx.rect(0, 0, this.data.canvasWidth, this.data.canvasHeight - 4)
  474. this.data.ctx.fillStyle = color
  475. this.data.ctx.fill() //设置填充
  476. },
  477. //预览
  478. async previewCanvasImg() {
  479. this.data.offCtx.rotate(-90 * Math.PI / 180);
  480. // offCtx.clearRect(0, 0, 100, 100)
  481. const srcImg = this.data.offCanvas.createImage()
  482. // 等待图片加载
  483. // console.log(this.data.canvas.toDataURL('image/png', 1))
  484. await new Promise(resolve => {
  485. srcImg.onload = resolve
  486. srcImg.src = this.data.canvas.toDataURL('image/png', 1) // 要加载的图片 url
  487. })
  488. let width = this.data.canvasWidth / this.data.dpr
  489. let height = this.data.canvasHeight / this.data.dpr
  490. this.data.offCtx.drawImage(srcImg, -width, 0, width, height)
  491. wx.canvasToTempFilePath({
  492. canvas: this.data.offCtx,
  493. fileType: 'jpg',
  494. quality: 1, //图片质量
  495. success(res) {
  496. console.log(res.tempFilePath, 'canvas生成图片地址')
  497. wx.previewImage({
  498. urls: [res.tempFilePath] //预览图片 数组
  499. })
  500. }
  501. })
  502. },
  503. //上传
  504. async uploadCanvasImg() {
  505. let that = this
  506. if (!this.data.isDraw) {
  507. wx.showToast({
  508. title: '请签名',
  509. icon: 'error'
  510. })
  511. return
  512. }
  513. // console.log('===============')
  514. const self = this
  515. this.data.offCtx.rotate(-90 * Math.PI / 180);
  516. // offCtx.clearRect(0, 0, 100, 100)
  517. const srcImg = this.data.offCanvas.createImage()
  518. // 等待图片加载
  519. // console.log(this.data.canvas.toDataURL('image/png', 1))
  520. await new Promise(resolve => {
  521. srcImg.onload = resolve
  522. srcImg.src = this.data.canvas.toDataURL('image/png', 1) // 要加载的图片 url
  523. console.log("await============")
  524. })
  525. console.log("await0============")
  526. console.log(srcImg)
  527. let width = this.data.canvasWidth / this.data.dpr
  528. let height = this.data.canvasHeight / this.data.dpr
  529. this.data.offCtx.drawImage(srcImg, -width, 0, width, height)
  530. console.log('===============2')
  531. console.log(srcImg)
  532. wx.canvasToTempFilePath({
  533. canvas: this.data.offCanvas,
  534. fileType: 'jpg', // png默认无背景是透明的,可以改jpg
  535. quality: 1, //图片质量
  536. success(res) {
  537. console.log(res)
  538. wx.getFileSystemManager().readFile({
  539. filePath: res.tempFilePath,
  540. success: result => {
  541. //返回临时文件路径
  542. console.log(res)
  543. const fileData = result.data;
  544. api._post({
  545. "classname": "system.attachment.huawei.OBS",
  546. "method": "getFileName",
  547. "content": {
  548. "filename": '客户签字',
  549. "filetype": 'jpg',
  550. "parentid": wx.getStorageSync('siteP').appfolderid
  551. }
  552. }).then(res => {
  553. if (res.code == '1') {
  554. that.uploadFile(res.data, fileData)
  555. }
  556. })
  557. },
  558. fail: console.error
  559. })
  560. },
  561. fail(res) {
  562. console.log(res)
  563. }
  564. })
  565. },
  566. uploadFile(res, data) {
  567. var that = this;
  568. wx.request({
  569. url: res.uploadurl,
  570. method: "PUT",
  571. data: data,
  572. header: {
  573. 'content-type': 'application/octet-stream'
  574. },
  575. success(a) {
  576. api._post({
  577. "classname": "system.attachment.huawei.OBS",
  578. "method": "uploadSuccess",
  579. "content": {
  580. "serialfilename": res.serialfilename
  581. }
  582. }).then(rs => {
  583. that.data.attachmentids.push(rs.data.attachmentids[0])
  584. that.filebindData()
  585. }).catch(err => {
  586. console.log(err)
  587. })
  588. }
  589. })
  590. },
  591. async filebindData() {
  592. let pages = getCurrentPages()[getCurrentPages().length - 2]
  593. const res = await api._post({
  594. "classname": "system.attachment.Attachment",
  595. "method": "createFileLink",
  596. "content": {
  597. "ownertable": pages.data.bindSignNameData.ownertable,
  598. "ownerid": pages.data.bindSignNameData.ownerid,
  599. "usetype": "signature",
  600. "attachmentids": this.data.attachmentids
  601. }
  602. })
  603. wx.navigateBack()
  604. },
  605. })