formatSvgToImg.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. // Render only first frame, ignoring animations and mouse.
  66. await v.start()
  67. // const blob = await canvas.convertToBlob()
  68. // const base64 = await blobToBase64(blob)
  69. const base64 = await canvasAddPadding(canvas, name)
  70. ctx.clearRect(0, 0, canvas.width, canvas.height)
  71. await v.stop()
  72. v = null
  73. return base64
  74. }
  75. const convertToBlob = (canvas: any, type = "image/png") => {
  76. return new Promise((resolve, reject) => {
  77. canvas.toBlob((blob: Blob) => {
  78. if (blob) {
  79. resolve(blob)
  80. } else {
  81. reject(new Error("转换失败"))
  82. }
  83. }, type)
  84. })
  85. }
  86. const canvasAddPadding = async (sourceCanvas: any, name: string) => {
  87. const targetCanvas = document.createElement("canvas")
  88. targetCanvas.width = sourceCanvas.width + 400
  89. targetCanvas.height = sourceCanvas.height + 200
  90. // 坐标(0,0) 表示从此处开始绘制,相当于偏移。
  91. const targetContext = targetCanvas.getContext("2d") as CanvasRenderingContext2D
  92. // const sourceContext = sourceCanvas.getContext("2d") as CanvasRenderingContext2D
  93. // 从源canvas中获取图像数据
  94. // const imageData = sourceContext.getImageData(0, 0, sourceCanvas.width, sourceCanvas.height)
  95. // 清空目标canvas
  96. targetContext.clearRect(0, 0, targetCanvas.width, targetCanvas.height)
  97. // 将图像数据绘制到目标canvas上,并添加边距
  98. // targetContext.putImageData(imageData, 200, 100)
  99. targetContext.fillStyle = "#fff"
  100. targetContext.fillRect(0, 0, targetCanvas.width, targetCanvas.height)
  101. // targetCanvas = await addMusicTitle(targetCanvas, name)
  102. // 小水印画布大小
  103. // const waterCtx = water.getContext("2d") as CanvasRenderingContext2D
  104. targetContext.font = `66pt Calibri`
  105. targetContext.fillStyle = "#000"
  106. targetContext.textAlign = "center"
  107. targetContext.drawImage(canvas, 200, 240)
  108. targetContext.fillText(name, canvas.width / 2, 200)
  109. const blob = await convertToBlob(targetCanvas)
  110. // const base64 = await blobToBase64(blob)
  111. targetContext.clearRect(0, 0, targetCanvas.width, targetCanvas.height)
  112. return blob
  113. }
  114. // // 获取文件blob格式
  115. export const imgToCanvas = async (url: string) => {
  116. const img = document.createElement("img")
  117. img.setAttribute("crossOrigin", "anonymous")
  118. // 为了处理base64 和 连接加载不同的
  119. if (url && typeof url == "string" && url.includes("data:image")) {
  120. img.src = url
  121. } else {
  122. img.src = url + `?t=${+new Date()}`
  123. }
  124. // 防止跨域引起的 Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
  125. await new Promise(resolve => (img.onload = resolve))
  126. // 创建canvas DOM元素,并设置其宽高和图片一样
  127. const canvas = document.createElement("canvas")
  128. canvas.width = img.width
  129. canvas.height = img.height
  130. // 坐标(0,0) 表示从此处开始绘制,相当于偏移。
  131. const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
  132. ctx.fillStyle = "rgb(255, 255, 255)"
  133. ctx.fillStyle = "#fff"
  134. ctx.fillRect(0, 0, img.width, img.height)
  135. ctx.drawImage(img, 0, 0)
  136. return canvas
  137. }