|  | @@ -0,0 +1,241 @@
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Created by louizhai on 17/6/30.
 | 
	
		
			
				|  |  | + * description: Use canvas to draw.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function Draw(canvas, degree, config = {}) {
 | 
	
		
			
				|  |  | +  if (!(this instanceof Draw)) {
 | 
	
		
			
				|  |  | +    return new Draw(canvas, config);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (!canvas) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  let { width, height } = window.getComputedStyle(canvas, null);
 | 
	
		
			
				|  |  | +  // width = width.replace('px', '');
 | 
	
		
			
				|  |  | +  height = height.replace('px', '');
 | 
	
		
			
				|  |  | +  width = height.replace('px', '')*2;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  this.canvas = canvas;
 | 
	
		
			
				|  |  | +  this.context = canvas.getContext('2d');
 | 
	
		
			
				|  |  | +  this.width = width;
 | 
	
		
			
				|  |  | +  this.height = height;
 | 
	
		
			
				|  |  | +  const context = this.context;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 根据设备像素比优化canvas绘图
 | 
	
		
			
				|  |  | +  const devicePixelRatio = window.devicePixelRatio;
 | 
	
		
			
				|  |  | +  if (devicePixelRatio) {
 | 
	
		
			
				|  |  | +    canvas.style.width = `${width}px`;
 | 
	
		
			
				|  |  | +    canvas.style.height = `${height}px`;
 | 
	
		
			
				|  |  | +    canvas.height = height * devicePixelRatio;
 | 
	
		
			
				|  |  | +    canvas.width = width * devicePixelRatio;
 | 
	
		
			
				|  |  | +    context.scale(devicePixelRatio, devicePixelRatio);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    canvas.width = width;
 | 
	
		
			
				|  |  | +    canvas.height = height;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  context.lineWidth = 6;
 | 
	
		
			
				|  |  | +  context.strokeStyle = 'black';
 | 
	
		
			
				|  |  | +  context.lineCap = 'round';
 | 
	
		
			
				|  |  | +  context.lineJoin = 'round';
 | 
	
		
			
				|  |  | +  Object.assign(context, config);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const { left, top } = canvas.getBoundingClientRect();
 | 
	
		
			
				|  |  | +  const point = {};
 | 
	
		
			
				|  |  | +  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);
 | 
	
		
			
				|  |  | +  // 移动端性能太弱, 去掉模糊以提高手写渲染速度
 | 
	
		
			
				|  |  | +  if (!isMobile) {
 | 
	
		
			
				|  |  | +    context.shadowBlur = 1;
 | 
	
		
			
				|  |  | +    context.shadowColor = 'black';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  let pressed = false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const paint = (signal) => {
 | 
	
		
			
				|  |  | +    switch (signal) {
 | 
	
		
			
				|  |  | +      case 1:
 | 
	
		
			
				|  |  | +        context.beginPath();
 | 
	
		
			
				|  |  | +        context.moveTo(point.x, point.y);
 | 
	
		
			
				|  |  | +      case 2:
 | 
	
		
			
				|  |  | +        context.lineTo(point.x, point.y);
 | 
	
		
			
				|  |  | +        context.stroke();
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      default:
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  const create = signal => (e) => {
 | 
	
		
			
				|  |  | +    e.preventDefault();
 | 
	
		
			
				|  |  | +    if (signal === 1) {
 | 
	
		
			
				|  |  | +      pressed = true;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (signal === 1 || pressed) {
 | 
	
		
			
				|  |  | +      e = isMobile ? e.touches[0] : e;
 | 
	
		
			
				|  |  | +      point.x = e.clientX - left;
 | 
	
		
			
				|  |  | +      point.y = e.clientY - top;
 | 
	
		
			
				|  |  | +      paint(signal);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  const start = create(1);
 | 
	
		
			
				|  |  | +  const move = create(2);
 | 
	
		
			
				|  |  | +  const requestAnimationFrame = window.requestAnimationFrame;
 | 
	
		
			
				|  |  | +  const optimizedMove = requestAnimationFrame ? (e) => {
 | 
	
		
			
				|  |  | +    requestAnimationFrame(() => {
 | 
	
		
			
				|  |  | +      move(e);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  } : move;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (isMobile) {
 | 
	
		
			
				|  |  | +    canvas.addEventListener('touchstart', start);
 | 
	
		
			
				|  |  | +    canvas.addEventListener('touchmove', optimizedMove);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    canvas.addEventListener('mousedown', start);
 | 
	
		
			
				|  |  | +    canvas.addEventListener('mousemove', optimizedMove);
 | 
	
		
			
				|  |  | +    ['mouseup', 'mouseleave'].forEach((event) => {
 | 
	
		
			
				|  |  | +      canvas.addEventListener(event, () => {
 | 
	
		
			
				|  |  | +        pressed = false;
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 重置画布坐标系
 | 
	
		
			
				|  |  | +  if (typeof degree === 'number') {
 | 
	
		
			
				|  |  | +    this.degree = degree;
 | 
	
		
			
				|  |  | +    context.rotate((degree * Math.PI) / 180);
 | 
	
		
			
				|  |  | +    switch (degree) {
 | 
	
		
			
				|  |  | +      case -90:
 | 
	
		
			
				|  |  | +        context.translate(-height, 0);
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case 90:
 | 
	
		
			
				|  |  | +        context.translate(0, -width);
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case -180:
 | 
	
		
			
				|  |  | +      case 180:
 | 
	
		
			
				|  |  | +        context.translate(-width, -height);
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      default:
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +Draw.prototype = {
 | 
	
		
			
				|  |  | +  scale(width, height, canvas = this.canvas) {
 | 
	
		
			
				|  |  | +    const w = canvas.width;
 | 
	
		
			
				|  |  | +    const h = canvas.height;
 | 
	
		
			
				|  |  | +    width = width || w;
 | 
	
		
			
				|  |  | +    height = height || h;
 | 
	
		
			
				|  |  | +    if (width !== w || height !== h) {
 | 
	
		
			
				|  |  | +      const tmpCanvas = document.createElement('canvas');
 | 
	
		
			
				|  |  | +      const tmpContext = tmpCanvas.getContext('2d');
 | 
	
		
			
				|  |  | +      tmpCanvas.width = width;
 | 
	
		
			
				|  |  | +      tmpCanvas.height = height;
 | 
	
		
			
				|  |  | +      tmpContext.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
 | 
	
		
			
				|  |  | +      canvas = tmpCanvas;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return canvas;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  rotate(degree, image = this.canvas) {
 | 
	
		
			
				|  |  | +    degree = ~~degree;
 | 
	
		
			
				|  |  | +    if (degree !== 0) {
 | 
	
		
			
				|  |  | +      const maxDegree = 180;
 | 
	
		
			
				|  |  | +      const minDegree = -90;
 | 
	
		
			
				|  |  | +      if (degree > maxDegree) {
 | 
	
		
			
				|  |  | +        degree = maxDegree;
 | 
	
		
			
				|  |  | +      } else if (degree < minDegree) {
 | 
	
		
			
				|  |  | +        degree = minDegree;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      const canvas = document.createElement('canvas');
 | 
	
		
			
				|  |  | +      const context = canvas.getContext('2d');
 | 
	
		
			
				|  |  | +      const height = image.height;
 | 
	
		
			
				|  |  | +      const width = image.width;
 | 
	
		
			
				|  |  | +      const degreePI = (degree * Math.PI) / 180;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      switch (degree) {
 | 
	
		
			
				|  |  | +        // 逆时针旋转90°
 | 
	
		
			
				|  |  | +        case -90:
 | 
	
		
			
				|  |  | +          canvas.width = height;
 | 
	
		
			
				|  |  | +          canvas.height = width;
 | 
	
		
			
				|  |  | +          context.rotate(degreePI);
 | 
	
		
			
				|  |  | +          context.drawImage(image, -width, 0);
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        // 顺时针旋转90°
 | 
	
		
			
				|  |  | +        case 90:
 | 
	
		
			
				|  |  | +          canvas.width = height;
 | 
	
		
			
				|  |  | +          canvas.height = width;
 | 
	
		
			
				|  |  | +          context.rotate(degreePI);
 | 
	
		
			
				|  |  | +          context.drawImage(image, 0, -height);
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        // 顺时针旋转180°
 | 
	
		
			
				|  |  | +        case 180:
 | 
	
		
			
				|  |  | +          canvas.width = width;
 | 
	
		
			
				|  |  | +          canvas.height = height;
 | 
	
		
			
				|  |  | +          context.rotate(degreePI);
 | 
	
		
			
				|  |  | +          context.drawImage(image, -width, -height);
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        default:
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      image = canvas;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return image;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  getPNGImage(canvas = this.canvas) {
 | 
	
		
			
				|  |  | +    console.log(canvas.width, canvas.height);
 | 
	
		
			
				|  |  | +    return canvas.toDataURL('image/png');
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  getJPGImage(canvas = this.canvas) {
 | 
	
		
			
				|  |  | +    return canvas.toDataURL('image/jpeg', 0.5);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  downloadPNGImage(image) {
 | 
	
		
			
				|  |  | +    const url = image.replace('image/png', 'image/octet-stream;Content-Disposition:attachment;filename=test.png');
 | 
	
		
			
				|  |  | +    window.location.href = url;
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  dataURLtoBlob(dataURL) {
 | 
	
		
			
				|  |  | +    const arr = dataURL.split(',');
 | 
	
		
			
				|  |  | +    const mime = arr[0].match(/:(.*?);/)[1];
 | 
	
		
			
				|  |  | +    const bStr = atob(arr[1]);
 | 
	
		
			
				|  |  | +    let n = bStr.length;
 | 
	
		
			
				|  |  | +    const u8arr = new Uint8Array(n);
 | 
	
		
			
				|  |  | +    while (n--) {
 | 
	
		
			
				|  |  | +      u8arr[n] = bStr.charCodeAt(n);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return new Blob([u8arr], { type: mime });
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  clear() {
 | 
	
		
			
				|  |  | +    let width;
 | 
	
		
			
				|  |  | +    let height;
 | 
	
		
			
				|  |  | +    switch (this.degree) {
 | 
	
		
			
				|  |  | +      case -90:
 | 
	
		
			
				|  |  | +      case 90:
 | 
	
		
			
				|  |  | +        width = this.height;
 | 
	
		
			
				|  |  | +        height = this.width;
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      default:
 | 
	
		
			
				|  |  | +        width = this.width;
 | 
	
		
			
				|  |  | +        height = this.height;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this.context.clearRect(0, 0, width, height);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +  upload(blob, url, success, failure) {
 | 
	
		
			
				|  |  | +    const formData = new FormData();
 | 
	
		
			
				|  |  | +    const xhr = new XMLHttpRequest();
 | 
	
		
			
				|  |  | +    xhr.withCredentials = true;
 | 
	
		
			
				|  |  | +    formData.append('id', id);
 | 
	
		
			
				|  |  | +    formData.append('image', blob, 'sign');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    xhr.open('POST', url, true);
 | 
	
		
			
				|  |  | +    xhr.setRequestHeader("x-csrf-token", csrf);
 | 
	
		
			
				|  |  | +    xhr.onload = () => {
 | 
	
		
			
				|  |  | +      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
 | 
	
		
			
				|  |  | +        success(xhr.responseText);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        failure();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    xhr.onerror = (e) => {
 | 
	
		
			
				|  |  | +      if (typeof failure === 'function') {
 | 
	
		
			
				|  |  | +        failure(e);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        console.log(`upload img error: ${e}`);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +    xhr.send(formData);
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +// export default Draw;
 |