UNPKG

@notthatnathan/use-canvas-image

Version:

A React hook for automatically exporting a canvas to img.

1 lines 5.34 kB
{"version":3,"file":"index.modern.mjs","sources":["../src/index.js"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { useInterval } from 'usehooks-ts'\n\n// by default, expect a positioned parent for the canvas\n// img will render behind it\nconst IMG_STYLE = 'position: absolute; top: 0; left: 0; z-index: 0;'\nconst CANVAS_STYLE = 'position: absolute; top: 0; left: 0; z-index: 1;'\n\n/**\n * Outputs an image from a canvas.\n * By default, the image renders behind the canvas on the z axis.\n * This behavior can be overridden with custom styles.\n *\n * @param {HTMLCanvasElement|String} canvas React ref/HTML element or string selector. If string, must be unique in dom and formatted as selector (#id, .class, [attr=\"val\"], etc)\n * @param {String} imgClassname for styling output image, space separated.\n * @param {String} canvasClassName for adding .fs-exclude, etc., and styling, space separated.\n * @param {Object} canvasAttributes for adding data-private, etc\n * @param {String} fileType what img format to output. default, recommended: image/jpeg\n * @param {Number} quality 0-1. the compression quality. only applies to image/jpeg or image/webp. default: 0.5\n * @param {Number} interval ms. how often to update the image. default: 100\n * @returns {Function} function to be called any time the canvas is drawn on\n */\nconst useCanvasImage = ({\n canvas,\n imgClassname = '',\n canvasClassname = '',\n canvasAttributes = {},\n fileType = 'image/jpeg', // fullstory doesn't like png\n quality = 0.7,\n interval = null,\n}) => {\n const canvasRef = useRef()\n const imgRef = useRef()\n\n const createImg = () => {\n // create image\n imgRef.current = new Image()\n\n // add classes to img\n if (imgClassname) imgRef.current.classList.add(...imgClassname.split(' '))\n\n // by default, img will be same size as canvas\n imgRef.current.style = IMG_STYLE\n }\n\n const updateCanvas = () => {\n // add attributes to canvas\n Object.keys(canvasAttributes).forEach(attr => {\n canvasRef.current.setAttribute(attr, canvasAttributes[attr])\n })\n\n // add classes to canvas\n if (canvasClassname)\n canvasRef.current.classList.add(...canvasClassname.split(' '))\n\n canvasRef.current.style = CANVAS_STYLE\n\n createImg()\n // insert img after canvas\n canvasRef.current.after(imgRef.current)\n }\n\n const createImgSrc = () => {\n requestAnimationFrame(() => {\n if (!canvasRef.current || !imgRef.current) return\n\n // generate new img src\n const dataUrl = canvasRef.current.toDataURL(fileType, quality)\n\n // set src on img\n imgRef.current.src = dataUrl\n })\n }\n\n useEffect(() => {\n let reselectTimeout\n\n const setCanvas = () => {\n if (typeof canvas === 'string') {\n // selector\n canvasRef.current = document.querySelector(canvas)\n } else if (canvas?.current instanceof HTMLCanvasElement) {\n // ref\n canvasRef.current = canvas.current\n } else if (canvas instanceof HTMLCanvasElement) {\n // canvas element\n canvasRef.current = canvas\n }\n\n if (canvasRef.current) {\n updateCanvas()\n } else {\n reselectTimeout = setTimeout(setCanvas, 100)\n }\n }\n\n setCanvas()\n\n // eslint-disable-next-line consistent-return\n return () => {\n if (reselectTimeout) clearTimeout(reselectTimeout)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // for interval-based update\n // passing `interval: null` disables\n // requires `preserveDrawingBuffer: true` on webgl context\n // don't use return function in this case\n useInterval(createImgSrc, interval)\n\n // function returned for explicit updates from component\n // ensure the return function isn't used in conjunction with intervals\n return interval === null || interval === false ? createImgSrc : null\n}\n\nexport default useCanvasImage\n"],"names":["useCanvasImage","canvas","imgClassname","canvasClassname","canvasAttributes","fileType","quality","interval","canvasRef","useRef","imgRef","createImgSrc","requestAnimationFrame","current","dataUrl","toDataURL","src","useEffect","reselectTimeout","setCanvas","document","querySelector","Object","keys","forEach","attr","setAttribute","classList","add","split","style","Image","after","setTimeout","clearTimeout","useInterval"],"mappings":"wFAKA,MAiBMA,EAAiB,EACrBC,SACAC,aAAAA,EAAe,GACfC,gBAAAA,EAAkB,GAClBC,iBAAAA,EAAmB,GACnBC,SAAAA,EAAW,aACXC,QAAAA,EAAU,GACVC,SAAAA,EAAW,SAEX,MAAeC,EAAGC,IACNC,EAAGD,IA8BGE,EAAG,KACnBC,sBAAsB,KACpB,IAAKJ,EAAUK,UAAYH,EAAOG,QAAS,OAG3C,MAAMC,EAAUN,EAAUK,QAAQE,UAAUV,EAAUC,GAGtDI,EAAOG,QAAQG,IAAMF,KA2CzB,OAvCAG,EAAU,KACR,IAAIC,EAEJ,MAAMC,EAAY,KACM,iBAAXlB,EAETO,EAAUK,QAAUO,SAASC,cAAcpB,IAClCA,aAAAA,EAAAA,EAAQY,qCAEjBL,EAAUK,QAAUZ,EAAOY,QAClBZ,iCAETO,EAAUK,QAAUZ,GAGlBO,EAAUK,SA1ChBS,OAAOC,KAAKnB,GAAkBoB,QAAQC,IACpCjB,EAAUK,QAAQa,aAAaD,EAAMrB,EAAiBqB,MAIpDtB,GACFK,EAAUK,QAAQc,UAAUC,OAAOzB,EAAgB0B,MAAM,MAE3DrB,EAAUK,QAAQiB,MAjDD,mDA8BjBpB,EAAOG,QAAU,IAAjBkB,MAGI7B,GAAcQ,EAAOG,QAAQc,UAAUC,OAAO1B,EAAa2B,MAAM,MAGrEnB,EAAOG,QAAQiB,MArCD,mDAsDdtB,EAAUK,QAAQmB,MAAMtB,EAAOG,UAiC3BK,EAAkBe,WAAWd,EAAW,MAO5C,OAHAA,IAGO,KACDD,GAAiBgB,aAAahB,KAGnC,IAMHiB,EAAYxB,EAAcJ,GAIN,OAAbA,IAAkC,IAAbA,EAAqBI,EAAe"}