@notthatnathan/use-canvas-image
Version:
A React hook for automatically exporting a canvas to img.
1 lines • 5.71 kB
Source Map (JSON)
{"version":3,"file":"index.esm.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","_ref","canvas","imgClassname","_ref$imgClassname","canvasClassname","_ref$canvasClassname","canvasAttributes","_ref$canvasAttributes","fileType","_ref$fileType","quality","_ref$quality","interval","_ref$interval","useRef","createImgSrc","requestAnimationFrame","canvasRef","current","imgRef","dataUrl","toDataURL","src","useEffect","_imgRef$current$class","document","querySelector","HTMLCanvasElement","Object","keys","forEach","attr","setAttribute","classList","add","split","style","Image","after","reselectTimeout","setTimeout","setCanvas","clearTimeout","useInterval"],"mappings":"wFAKA,IAiBoBA,EAAG,SAAAC,OAQjBC,EAAAD,EAPJC,OACAC,EAAAA,EAAAA,aAAAA,OAMI,IAAAC,EANW,GACfC,EAAAA,EAAAA,EAAAA,gBAAAA,OAKI,IAAAC,EALc,GAClBC,EAAAA,EAAAA,EAAAA,iBAAAA,OAII,IAAAC,EAJe,GACnBC,EAAAA,EAAAA,EAAAA,SAAAA,OAGI,IAAAC,EAHO,aACXC,EAAAA,EAAAA,EAAAA,QAAAA,OAEI,IAAAC,EAFM,GACVC,EAAAA,EAAAA,EAAAA,SAAAA,OACI,IAAAC,EADO,KAEXA,IAAkBC,MACHA,IA8BTC,EAAe,WACnBC,sBAAsB,WACpB,GAAKC,EAAUC,SAAYC,EAAOD,QAAlC,CAGA,IAAaE,EAAGH,EAAUC,QAAQG,UAAUb,EAAUE,GAGtDS,EAAOD,QAAQI,IAAMF,MA2CzB,OAvCAG,EAAU,WACR,MAwBA,OAtBkB,aAhCC,MAXGC,EA4CE,iBAAXvB,EAETgB,EAAUC,QAAUO,SAASC,cAAczB,IAClCA,eAAAA,EAAQiB,mBAAmBS,kBAEpCV,EAAUC,QAAUjB,EAAOiB,QAClBjB,aAAkB0B,oBAE3BV,EAAUC,QAAUjB,GAGlBgB,EAAUC,SA1ChBU,OAAOC,KAAKvB,GAAkBwB,QAAQ,SAAAC,GACpCd,EAAUC,QAAQc,aAAaD,EAAMzB,EAAiByB,MAIpD3B,IACFa,EAAAA,EAAUC,QAAQe,WAAUC,YAAO9B,EAAgB+B,MAAM,MAE3DlB,EAAUC,QAAQkB,MAjDD,mDA8BjBjB,EAAOD,QAAU,IAAImB,MAGjBnC,IAAcsB,EAAAL,EAAOD,QAAQe,WAAUC,IAAOhC,MAAAA,EAAAA,EAAaiC,MAAM,MAGrEhB,EAAOD,QAAQkB,MArCD,mDAsDdnB,EAAUC,QAAQoB,MAAMnB,EAAOD,UAiC3BqB,EAAkBC,WAAWC,EAAW,KAI5CA,cAIMF,GAAiBG,aAAaH,KAGnC,IAMHI,EAAY5B,EAAcH,GAIN,OAAbA,IAAkC,IAAbA,EAAqBG,EAAe"}