formatSvgToImg.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import { Canvg, presets } from "canvg"
  2. // https://gist.githubusercontent.com/n1ru4l/9c7eff52fe084d67ff15ae6b0af5f171/raw/da9fe36d72171d4e36b92aced587b48dc5182792/offscreen-canvas-polyfill.js
  3. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  4. // @ts-ignore
  5. if (!window.OffscreenCanvas) {
  6. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  7. // @ts-ignore
  8. window.OffscreenCanvas = class OffscreenCanvas {
  9. canvas: HTMLCanvasElement
  10. constructor(width: number, height: number) {
  11. this.canvas = document.createElement("canvas")
  12. this.canvas.width = width
  13. this.canvas.height = height
  14. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  15. // @ts-ignore
  16. this.canvas.convertToBlob = () => {
  17. return new Promise(resolve => {
  18. this.canvas.toBlob(resolve)
  19. })
  20. }
  21. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  22. // @ts-ignore
  23. return this.canvas
  24. }
  25. }
  26. }
  27. const preset: any = presets.offscreen()
  28. export const blobToBase64 = (blob: any) => {
  29. return new Promise(resolve => {
  30. const reader = new FileReader()
  31. reader.onloadend = () => resolve(reader.result)
  32. reader.readAsDataURL(blob)
  33. })
  34. }
  35. export const addMusicTitle = (canvas: any, title: any) => {
  36. canvas.getContext("2d")
  37. const water = document.createElement("canvas")
  38. // 小水印画布大小
  39. water.width = canvas.width
  40. water.height = canvas.height + 120
  41. const waterCtx = water.getContext("2d") as CanvasRenderingContext2D
  42. waterCtx.font = `66pt Calibri`
  43. waterCtx.fillStyle = "#000"
  44. waterCtx.textAlign = "center"
  45. waterCtx.drawImage(canvas, 0, 40)
  46. waterCtx.fillText(title, canvas.width / 2, 160)
  47. return water
  48. }
  49. let canvas = null as any
  50. export const svgtoblob = async (svg: any, width: any, height: any, name: string) => {
  51. if (!canvas) {
  52. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  53. // @ts-ignore
  54. canvas = new OffscreenCanvas(width, height)
  55. }
  56. const ctx = canvas.getContext("2d")!
  57. let v: any = await Canvg.fromString(ctx!, svg, preset)
  58. /**
  59. * Resize SVG to fit in given size.
  60. * @param width
  61. * @param height
  62. * @param preserveAspectRatio
  63. */
  64. // v.resize(width * 2, height * 2, "xMidYMid meet")
  65. v.resize(width / 1.2, height / 1.2, "xMidYMid meet")
  66. // Render only first frame, ignoring animations and mouse.
  67. await v.start()
  68. // const blob = await canvas.convertToBlob()
  69. // const base64 = await blobToBase64(blob)
  70. const base64 = await canvasAddPadding(canvas, name)
  71. ctx.clearRect(0, 0, canvas.width, canvas.height)
  72. await v.stop()
  73. v = null
  74. return base64
  75. }
  76. // export const svgtopng = async (svg: any, width: any, height: any) => {
  77. // let canvas: any = new OffscreenCanvas(width, height);
  78. // const ctx = canvas.getContext("2d")!;
  79. // let v: any = await Canvg.fromString(ctx!, svg, preset);
  80. // /**
  81. // * Resize SVG to fit in given size.
  82. // * @param width
  83. // * @param height
  84. // * @param preserveAspectRatio
  85. // */
  86. // v.resize(width / 1.2, height / 1.2, "xMidYMid meet");
  87. // // Render only first frame, ignoring animations and mouse.
  88. // await v.start();
  89. // let blob: any = await canvas.convertToBlob();
  90. // const base64 = await blobToBase64(blob);
  91. // ctx.clearRect(0, 0, canvas.width, canvas.height);
  92. // canvas = null;
  93. // v.stop();
  94. // v = null;
  95. // blob = null;
  96. // return base64;
  97. // };
  98. const convertToBlob = (canvas: any, type = "image/png") => {
  99. return new Promise((resolve, reject) => {
  100. canvas.toBlob((blob: Blob) => {
  101. if (blob) {
  102. resolve(blob)
  103. } else {
  104. reject(new Error("转换失败"))
  105. }
  106. }, type)
  107. })
  108. }
  109. const canvasAddPadding = async (sourceCanvas: any, name: string) => {
  110. const targetCanvas = document.createElement("canvas")
  111. targetCanvas.width = sourceCanvas.width + 400
  112. targetCanvas.height = sourceCanvas.height + 200
  113. // 坐标(0,0) 表示从此处开始绘制,相当于偏移。
  114. const targetContext = targetCanvas.getContext("2d") as CanvasRenderingContext2D
  115. // const sourceContext = sourceCanvas.getContext("2d") as CanvasRenderingContext2D
  116. // 从源canvas中获取图像数据
  117. // const imageData = sourceContext.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height)
  118. // 清空目标canvas
  119. targetContext.clearRect(0, 0, targetCanvas.width, targetCanvas.height)
  120. // 将图像数据绘制到目标canvas上,并添加边距
  121. // targetContext.putImageData(imageData, 200, 100)
  122. targetContext.fillStyle = "#fff"
  123. targetContext.fillRect(0, 0, targetCanvas.width, targetCanvas.height)
  124. // targetCanvas = await addMusicTitle(targetCanvas, name)
  125. // 小水印画布大小
  126. // const waterCtx = water.getContext("2d") as CanvasRenderingContext2D
  127. targetContext.font = `66pt Calibri`
  128. targetContext.fillStyle = "#000"
  129. targetContext.textAlign = "center"
  130. targetContext.drawImage(sourceCanvas, 200, 240)
  131. targetContext.fillText(name, targetCanvas.width / 2, 200)
  132. const blob = await convertToBlob(targetCanvas)
  133. // const base64 = await blobToBase64(blob)
  134. targetContext.clearRect(0, 0, targetCanvas.width, targetCanvas.height)
  135. return blob
  136. }
  137. export const canvasAddTitle = async (sourceCanvas: any, name: string) => {
  138. const targetCanvas = document.createElement("canvas")
  139. targetCanvas.width = sourceCanvas.width
  140. targetCanvas.height = sourceCanvas.height + 90
  141. // 坐标(0,0) 表示从此处开始绘制,相当于偏移。
  142. const targetContext = targetCanvas.getContext("2d") as CanvasRenderingContext2D
  143. // const sourceContext = sourceCanvas.getContext("2d") as CanvasRenderingContext2D
  144. // 从源canvas中获取图像数据
  145. // const imageData = sourceContext.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height)
  146. // 清空目标canvas
  147. targetContext.clearRect(0, 0, targetCanvas.width, targetCanvas.height)
  148. // 将图像数据绘制到目标canvas上,并添加边距
  149. // targetContext.putImageData(imageData, 200, 100)
  150. targetContext.fillStyle = "#fff"
  151. targetContext.fillRect(0, 0, targetCanvas.width, targetCanvas.height)
  152. // targetCanvas = await addMusicTitle(targetCanvas, name)
  153. // 小水印画布大小
  154. // const waterCtx = water.getContext("2d") as CanvasRenderingContext2D
  155. targetContext.font = `30pt Calibri`
  156. targetContext.fillStyle = "#000"
  157. targetContext.textAlign = "center"
  158. targetContext.drawImage(sourceCanvas, 0, 70)
  159. targetContext.fillText(name, targetCanvas.width / 2, 100)
  160. const blob = await convertToBlob(targetCanvas)
  161. // const base64 = await blobToBase64(blob)
  162. targetContext.clearRect(0, 0, targetCanvas.width, targetCanvas.height)
  163. return blob
  164. }
  165. export const convasToImg = (canvas: any) => {
  166. return canvas.toDataURL("image/png")
  167. }
  168. // // 获取文件blob格式
  169. export const imgToCanvas = async (url: string) => {
  170. const img = document.createElement("img")
  171. img.setAttribute("crossOrigin", "anonymous")
  172. // 为了处理base64 和 连接加载不同的
  173. if (url && typeof url == "string" && url.includes("data:image")) {
  174. img.src = url
  175. } else {
  176. img.src = url + `?t=${+new Date()}`
  177. }
  178. // 防止跨域引起的 Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
  179. await new Promise(resolve => (img.onload = resolve))
  180. // 创建canvas DOM元素,并设置其宽高和图片一样
  181. const canvas = document.createElement("canvas")
  182. canvas.width = img.width
  183. canvas.height = img.height
  184. // 坐标(0,0) 表示从此处开始绘制,相当于偏移。
  185. const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
  186. ctx.fillStyle = "rgb(255, 255, 255)"
  187. ctx.fillStyle = "#fff"
  188. ctx.fillRect(0, 0, img.width, img.height)
  189. ctx.drawImage(img, 0, 0)
  190. return canvas
  191. }