draw.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * Created by louizhai on 17/6/30.
  3. * description: Use canvas to draw.
  4. */
  5. function Draw(canvas, degree, config = {}) {
  6. if (!(this instanceof Draw)) {
  7. return new Draw(canvas, config);
  8. }
  9. if (!canvas) {
  10. return;
  11. }
  12. let { width, height } = window.getComputedStyle(canvas, null);
  13. // width = width.replace('px', '');
  14. height = height.replace('px', '');
  15. width = height.replace('px', '')*2;
  16. this.canvas = canvas;
  17. this.context = canvas.getContext('2d');
  18. this.width = width;
  19. this.height = height;
  20. const context = this.context;
  21. // 根据设备像素比优化canvas绘图
  22. const devicePixelRatio = window.devicePixelRatio;
  23. if (devicePixelRatio) {
  24. canvas.style.width = `${width}px`;
  25. canvas.style.height = `${height}px`;
  26. canvas.height = height * devicePixelRatio;
  27. canvas.width = width * devicePixelRatio;
  28. context.scale(devicePixelRatio, devicePixelRatio);
  29. } else {
  30. canvas.width = width;
  31. canvas.height = height;
  32. }
  33. context.lineWidth = 8;
  34. context.strokeStyle = 'black';
  35. context.lineCap = 'round';
  36. context.lineJoin = 'round';
  37. Object.assign(context, config);
  38. const { left, top } = canvas.getBoundingClientRect();
  39. const point = {};
  40. const isMobile = /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/i.test(navigator.userAgent);
  41. // 移动端性能太弱, 去掉模糊以提高手写渲染速度
  42. if (!isMobile) {
  43. context.shadowBlur = 1;
  44. context.shadowColor = 'black';
  45. }
  46. let pressed = false;
  47. const paint = (signal) => {
  48. switch (signal) {
  49. case 1:
  50. context.beginPath();
  51. context.moveTo(point.x, point.y);
  52. case 2:
  53. context.lineTo(point.x, point.y);
  54. context.stroke();
  55. break;
  56. default:
  57. }
  58. };
  59. const create = signal => (e) => {
  60. e.preventDefault();
  61. if (signal === 1) {
  62. pressed = true;
  63. }
  64. if (signal === 1 || pressed) {
  65. e = isMobile ? e.touches[0] : e;
  66. point.x = e.clientX - left;
  67. point.y = e.clientY - top;
  68. paint(signal);
  69. }
  70. };
  71. const start = create(1);
  72. const move = create(2);
  73. const requestAnimationFrame = window.requestAnimationFrame;
  74. const optimizedMove = requestAnimationFrame ? (e) => {
  75. requestAnimationFrame(() => {
  76. move(e);
  77. });
  78. } : move;
  79. if (isMobile) {
  80. canvas.addEventListener('touchstart', start);
  81. canvas.addEventListener('touchmove', optimizedMove);
  82. } else {
  83. canvas.addEventListener('mousedown', start);
  84. canvas.addEventListener('mousemove', optimizedMove);
  85. ['mouseup', 'mouseleave'].forEach((event) => {
  86. canvas.addEventListener(event, () => {
  87. pressed = false;
  88. });
  89. });
  90. }
  91. // 重置画布坐标系
  92. if (typeof degree === 'number') {
  93. this.degree = degree;
  94. context.rotate((degree * Math.PI) / 180);
  95. switch (degree) {
  96. case -90:
  97. context.translate(-height, 0);
  98. break;
  99. case 90:
  100. context.translate(0, -width);
  101. break;
  102. case -180:
  103. case 180:
  104. context.translate(-width, -height);
  105. break;
  106. default:
  107. }
  108. }
  109. }
  110. Draw.prototype = {
  111. scale(width, height, canvas = this.canvas) {
  112. const w = canvas.width;
  113. const h = canvas.height;
  114. width = width || w;
  115. height = height || h;
  116. if (width !== w || height !== h) {
  117. const tmpCanvas = document.createElement('canvas');
  118. const tmpContext = tmpCanvas.getContext('2d');
  119. tmpCanvas.width = width;
  120. tmpCanvas.height = height;
  121. tmpContext.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
  122. canvas = tmpCanvas;
  123. }
  124. return canvas;
  125. },
  126. rotate(degree, image = this.canvas) {
  127. degree = ~~degree;
  128. if (degree !== 0) {
  129. const maxDegree = 180;
  130. const minDegree = -90;
  131. if (degree > maxDegree) {
  132. degree = maxDegree;
  133. } else if (degree < minDegree) {
  134. degree = minDegree;
  135. }
  136. const canvas = document.createElement('canvas');
  137. const context = canvas.getContext('2d');
  138. const height = image.height;
  139. const width = image.width;
  140. const degreePI = (degree * Math.PI) / 180;
  141. switch (degree) {
  142. // 逆时针旋转90°
  143. case -90:
  144. canvas.width = height;
  145. canvas.height = width;
  146. context.rotate(degreePI);
  147. context.drawImage(image, -width, 0);
  148. break;
  149. // 顺时针旋转90°
  150. case 90:
  151. canvas.width = height;
  152. canvas.height = width;
  153. context.rotate(degreePI);
  154. context.drawImage(image, 0, -height);
  155. break;
  156. // 顺时针旋转180°
  157. case 180:
  158. canvas.width = width;
  159. canvas.height = height;
  160. context.rotate(degreePI);
  161. context.drawImage(image, -width, -height);
  162. break;
  163. default:
  164. }
  165. image = canvas;
  166. }
  167. return image;
  168. },
  169. getPNGImage(canvas = this.canvas) {
  170. console.log(canvas.width, canvas.height);
  171. return canvas.toDataURL('image/png');
  172. },
  173. getJPGImage(canvas = this.canvas) {
  174. return canvas.toDataURL('image/jpeg', 0.5);
  175. },
  176. downloadPNGImage(image) {
  177. const url = image.replace('image/png', 'image/octet-stream;Content-Disposition:attachment;filename=test.png');
  178. window.location.href = url;
  179. },
  180. dataURLtoBlob(dataURL) {
  181. const arr = dataURL.split(',');
  182. const mime = arr[0].match(/:(.*?);/)[1];
  183. const bStr = atob(arr[1]);
  184. let n = bStr.length;
  185. const u8arr = new Uint8Array(n);
  186. while (n--) {
  187. u8arr[n] = bStr.charCodeAt(n);
  188. }
  189. return new Blob([u8arr], { type: mime });
  190. },
  191. clear() {
  192. let width;
  193. let height;
  194. switch (this.degree) {
  195. case -90:
  196. case 90:
  197. width = this.height;
  198. height = this.width;
  199. break;
  200. default:
  201. width = this.width;
  202. height = this.height;
  203. }
  204. this.context.clearRect(0, 0, width, height);
  205. },
  206. upload(blob, url, success, failure) {
  207. const formData = new FormData();
  208. const xhr = new XMLHttpRequest();
  209. xhr.withCredentials = true;
  210. formData.append('id', id);
  211. formData.append('from', from);
  212. formData.append('image', blob, 'sign');
  213. xhr.open('POST', url, true);
  214. xhr.setRequestHeader("x-csrf-token", csrf);
  215. xhr.onload = () => {
  216. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
  217. success(xhr.responseText);
  218. } else {
  219. failure();
  220. }
  221. };
  222. xhr.onerror = (e) => {
  223. if (typeof failure === 'function') {
  224. failure(e);
  225. } else {
  226. console.log(`upload img error: ${e}`);
  227. }
  228. };
  229. xhr.send(formData);
  230. },
  231. uploadBase64(base64, url, success, failure) {
  232. const xhr = new XMLHttpRequest();
  233. xhr.withCredentials = true;
  234. const data = encodeURIComponent('id') + '=' + encodeURIComponent(id) + '&' + encodeURIComponent('sign_base64') + '=' + encodeURIComponent(base64);
  235. xhr.open('POST', url, true);
  236. xhr.setRequestHeader("x-csrf-token", csrf);
  237. xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=utf-8");
  238. xhr.onload = () => {
  239. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
  240. success(xhr.responseText);
  241. } else {
  242. failure();
  243. }
  244. };
  245. xhr.onerror = (e) => {
  246. if (typeof failure === 'function') {
  247. failure(e);
  248. } else {
  249. console.log(`upload img error: ${e}`);
  250. }
  251. };
  252. xhr.send(data);
  253. },
  254. };
  255. // export default Draw;