import html2canvas from 'html2canvas' ; const Canvas2Image = ( ) => { const win = window ; const downloadMime = 'image/octet-stream' ; function scaleCanvas ( canvas , width , height ) { const w = canvas . width ; const h = canvas . height ; if ( width === undefined ) { width = w ; } if ( height === undefined ) { height = h ; } const retCanvas = win . document . createElement ( 'canvas' ) ; const retCtx = retCanvas . getContext ( '2d' ) ; retCanvas . width = width ; retCanvas . height = height ; retCtx . drawImage ( canvas , 0 , 0 , w , h , 0 , 0 , width , height ) ; return retCanvas ; } function getDataURL ( canvas , type , width , height ) { canvas = scaleCanvas ( canvas , width , height ) ; return canvas . toDataURL ( type ) ; } function saveFile ( strData , filename ) { const saveLink = win . document . createElementNS ( 'http://www.w3.org/1999/xhtml' , 'a' ) ; saveLink . href = strData ; saveLink . download = filename ; const event = win . document . createEvent ( 'MouseEvents' ) ; event . initMouseEvent ( 'click' , true , false , window , 0 , 0 , 0 , 0 , 0 , false , false , false , false , 0 , null ) ; saveLink . dispatchEvent ( event ) ; } function genImage ( strData ) { return strData ; } function fixType ( type ) { type = type . toLowerCase ( ) . replace ( /jpg/i , 'jpeg' ) ; const r = type . match ( /png|jpeg|bmp|gif/ ) [ 0 ] ; return `image/${ r }` ; } function encodeData ( data ) { if ( ! window . btoa ) { return 'btoa undefined' ; } let str = '' ; if ( typeof data === 'string' ) { str = data ; } else { for ( let i2 = 0 ; i2 < data . length ; i2 += 1 ) { str += String . fromCharCode ( data [ i2 ] ) ; } } return btoa ( str ) ; } function getImageData ( canvas ) { const w = canvas . width ; const h = canvas . height ; return canvas . getContext ( '2d' ) . getImageData ( 0 , 0 , w , h ) ; } function makeURI ( strData , type ) { return `data: ${ type };base64,${ strData }` ; } /**
     * create bitmap image
     * 按照规则生成图片响应头和响应体
     */ const genBitmapImage = ( oData ) => { const biWidth = oData . width ; const biHeight = oData . height ; const biSizeImage = biWidth * biHeight * 3 ; const bfSize = biSizeImage + 54 ; const BITMAPFILEHEADER = [ 0x42 , 0x4D , bfSize & 0xff , bfSize >> 8 & 0xff , bfSize >> 16 & 0xff , bfSize >> 24 & 0xff , 0 , 0 , 0 , 0 , 54 , 0 , 0 , 0 , ] ; const BITMAPINFOHEADER = [ 40 , 0 , 0 , 0 , biWidth & 0xff , biWidth >> 8 & 0xff , biWidth >> 16 & 0xff , biWidth >> 24 & 0xff , biHeight & 0xff , biHeight >> 8 & 0xff , biHeight >> 16 & 0xff , biHeight >> 24 & 0xff , 1 , 0 , 24 , 0 , 0 , 0 , 0 , 0 , biSizeImage & 0xff , biSizeImage >> 8 & 0xff , biSizeImage >> 16 & 0xff , biSizeImage >> 24 & 0xff , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , ] ; const iPadding = ( 4 - ( ( biWidth * 3 ) % 4 ) ) % 4 ; const aImgData = oData . data ; let strPixelData = '' ; const biWidth4 = biWidth << 2 ; let y = biHeight ; y -= 1 ; do { const iOffsetY = biWidth4 * ( y - 1 ) ; let strPixelRow = '' ; for ( let x = 0 ; x < biWidth ; x += 1 ) { const iOffsetX = x << 2 ; strPixelRow += String . fromCharCode ( aImgData [ iOffsetY + iOffsetX + 2 ] ) + String . fromCharCode ( aImgData [ iOffsetY + iOffsetX + 1 ] ) + String . fromCharCode ( aImgData [ iOffsetY + iOffsetX ] ) ; } for ( let c = 0 ; c < iPadding ; c += 1 ) { strPixelRow += String . fromCharCode ( 0 ) ; } strPixelData += strPixelRow ; y -= 1 ; } while ( y ) ; const strEncoded = encodeData ( BITMAPFILEHEADER . concat ( BITMAPINFOHEADER ) ) + encodeData ( strPixelData ) ; return strEncoded ; } ; param { [ obj ] } canvas param { [ Number ] } width param { [ Number ] } height param { [ String ] } type param { [ String ] } filename return { [ type ] } const saveAsImage = ( canvas , width , height , type , filename ) => { if ( typeof canvas === 'string' ) { canvas = win . document . getElementById ( canvas ) ; } if ( type === undefined ) { type = 'png' ; } filename = filename === undefined || filename . length === 0 ? `${ Date . now ( ) }.${ type }` : `${ filename }.${ type }` ; type = fixType ( type ) ; if ( /bmp/ . test ( type ) ) { const data = getImageData ( scaleCanvas ( canvas , width , height ) ) ; const strData = genBitmapImage ( data ) ; saveFile ( makeURI ( strData , downloadMime ) , filename ) ; } else { const strData = getDataURL ( canvas , type , width , height ) ; saveFile ( strData . replace ( type , downloadMime ) , filename ) ; } } ; const convertToImage = ( canvas , width , height , type ) => { if ( typeof canvas === 'string' ) { canvas = win . document . getElementById ( canvas ) ; } if ( type === undefined ) { type = 'png' ; } type = fixType ( type ) ; if ( /bmp/ . test ( type ) ) { const data = getImageData ( scaleCanvas ( canvas , width , height ) ) ; const strData = genBitmapImage ( data ) ; return genImage ( makeURI ( strData , 'image/bmp' ) ) ; } const strData = getDataURL ( canvas , type , width , height ) ; return genImage ( strData ) ; } ; const getPixelRatio = ( context ) => { const backingStore = context . backingStorePixelRatio || context . webkitBackingStorePixelRatio || context . mozBackingStorePixelRatio || context . msBackingStorePixelRatio || context . oBackingStorePixelRatio || context . backingStorePixelRatio || 1 ; return ( window . devicePixelRatio || 1 ) / backingStore ; } ; const getHighPixels = ( data , width , height ) => { width = parseInt ( width , 10 ) ; height = parseInt ( height , 10 ) ; const canvas = win . document . querySelector ( 'canvas' ) ; const ctx = canvas . getContext ( '2d' ) ; const ratio = getPixelRatio ( ctx ) ; width *= ratio ; height *= ratio ; return { data : data , width : width , height : height , ratio : ratio , } ; } ; const getCanvasImg = ( selector1 , selector2 , imgType ) => { const canvasHtml1 = win . document . querySelector ( selector1 ) ; const canvasHtml2 = win . document . querySelector ( selector2 ) ; return new Promise ( ( resolve ) => { win . document . body . style . overflow = 'auto' ; html2canvas ( canvasHtml1 , { scale : 2 , allowTaint : true , logging : false , useCORS : true , removeContainer : true , } ) . then ( ( canvas ) => { canvasHtml2 . appendChild ( canvas ) ; const width = canvas . style . width . substr ( 0 , canvas . style . width . length - 2 ) ; const height = canvas . style . height . substr ( 0 , canvas . style . height . length - 2 ) ; const canvasData = getHighPixels ( canvas , width , height ) ; const base64Data = convertToImage ( canvasData . data , canvasData . width , canvasData . height , imgType ) ; win . document . body . style . overflow = 'hidden' ; resolve ( base64Data ) ; } ) ; } ) ; } ; return { getHighPixels : getHighPixels , saveAsImage : saveAsImage , convertToImage : convertToImage , getCanvasImg : getCanvasImg , } ; } ; export default Canvas2Image ;