index.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import baseComponent from '../helpers/baseComponent'
  2. import classNames from '../helpers/libs/classNames'
  3. import styleToCssString from '../helpers/libs/styleToCssString'
  4. import { getSystemInfoSync } from '../helpers/hooks/useNativeAPI'
  5. import { getCanvasRef, toDataURL, createImage, downloadImage } from '../helpers/hooks/useCanvasAPI'
  6. baseComponent({
  7. properties: {
  8. prefixCls: {
  9. type: String,
  10. value: 'wux-water-mark',
  11. },
  12. content: {
  13. optionalTypes: [Array, String],
  14. type: String,
  15. value: '',
  16. },
  17. fontColor: {
  18. type: String,
  19. value: 'rgba(0, 0, 0, .15)',
  20. },
  21. fontStyle: {
  22. type: String,
  23. value: 'normal',
  24. },
  25. fontFamily: {
  26. type: String,
  27. value: 'sans-serif',
  28. },
  29. fontWeight: {
  30. type: String,
  31. value: 'normal',
  32. },
  33. fontSize: {
  34. type: Number,
  35. value: 14,
  36. },
  37. fullPage: {
  38. type: Boolean,
  39. value: true,
  40. },
  41. gapX: {
  42. type: Number,
  43. value: 24,
  44. },
  45. gapY: {
  46. type: Number,
  47. value: 48,
  48. },
  49. width: {
  50. type: Number,
  51. value: 120,
  52. },
  53. height: {
  54. type: Number,
  55. value: 64,
  56. },
  57. image: {
  58. type: String,
  59. value: '',
  60. },
  61. imageHeight: {
  62. type: Number,
  63. value: 64,
  64. },
  65. imageWidth: {
  66. type: Number,
  67. value: 128,
  68. },
  69. rotate: {
  70. type: Number,
  71. value: -22,
  72. },
  73. zIndex: {
  74. type: Number,
  75. value: 2000,
  76. },
  77. },
  78. data: {
  79. wrapStyle: '',
  80. base64Url: '',
  81. },
  82. observers: {
  83. ['zIndex, gapX, width, base64Url'](...args) {
  84. this.updateStyle(...args)
  85. },
  86. ['prefixCls, gapX, gapY, rotate, fontStyle, fontWeight, width, height, fontFamily, fontColor, image, imageWidth, imageHeight, content, fontSize'](...args) {
  87. this.setBase64Url(...args)
  88. },
  89. },
  90. computed: {
  91. classes: ['prefixCls, fullPage', function(prefixCls, fullPage) {
  92. const wrap = classNames(prefixCls, {
  93. [`${prefixCls}--full-page`]: fullPage,
  94. })
  95. const canvas = `${prefixCls}__canvas`
  96. return {
  97. wrap,
  98. canvas,
  99. }
  100. }],
  101. },
  102. methods: {
  103. setBase64Url(...args) {
  104. const [
  105. prefixCls,
  106. gapX,
  107. gapY,
  108. rotate,
  109. fontStyle,
  110. fontWeight,
  111. width,
  112. height,
  113. fontFamily,
  114. fontColor,
  115. image,
  116. imageWidth,
  117. imageHeight,
  118. content,
  119. fontSize,
  120. ] = args
  121. this.createCanvasContext({
  122. prefixCls,
  123. gapX,
  124. gapY,
  125. rotate,
  126. fontStyle,
  127. fontWeight,
  128. width,
  129. height,
  130. fontFamily,
  131. fontColor,
  132. image,
  133. imageWidth,
  134. imageHeight,
  135. content,
  136. fontSize,
  137. })
  138. },
  139. createCanvasContext(props) {
  140. const {
  141. prefixCls,
  142. gapX,
  143. gapY,
  144. rotate,
  145. fontStyle,
  146. fontWeight,
  147. width,
  148. height,
  149. fontFamily,
  150. fontColor,
  151. image,
  152. imageWidth,
  153. imageHeight,
  154. content,
  155. fontSize,
  156. } = props
  157. const setBase64Url = (base64Url) => {
  158. if (props.base64Url !== base64Url) {
  159. this.setData({
  160. base64Url,
  161. })
  162. this.triggerEvent('load', { base64Url })
  163. }
  164. }
  165. const canvasId = `${prefixCls}__canvas`
  166. const renderCanvas = (imageUrl) => getCanvasRef(canvasId, this).then((canvas) => {
  167. const ctx = canvas.getContext('2d')
  168. const ratio = getSystemInfoSync(['window']).pixelRatio
  169. const canvasWidth = (gapX + width) * ratio
  170. const canvasHeight = (gapY + height) * ratio
  171. const markWidth = width * ratio
  172. const markHeight = height * ratio
  173. canvas.width = canvasWidth
  174. canvas.height = canvasHeight
  175. if (imageUrl) {
  176. ctx.translate(markWidth / 2, markHeight / 2)
  177. ctx.rotate((Math.PI / 180) * Number(rotate))
  178. return createImage({
  179. imageWidth,
  180. imageHeight,
  181. imageUrl,
  182. }, canvas).then(() => (
  183. toDataURL({ width, height }, canvas)
  184. .then((base64Url) => {
  185. ctx.restore()
  186. return base64Url
  187. })
  188. ))
  189. } else if (content) {
  190. ctx.textBaseline = 'middle'
  191. ctx.textAlign = 'center'
  192. ctx.translate(markWidth / 2, markHeight / 2)
  193. ctx.rotate((Math.PI / 180) * Number(rotate))
  194. const markSize = Number(fontSize) * ratio
  195. ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`
  196. ctx.fillStyle = fontColor
  197. if (Array.isArray(content)) {
  198. content.forEach((item, index) =>
  199. ctx.fillText(item, 0, index * markSize)
  200. )
  201. } else {
  202. ctx.fillText(content, 0, 0)
  203. }
  204. return toDataURL({ width, height }, canvas)
  205. .then((base64Url) => {
  206. ctx.restore()
  207. return base64Url
  208. })
  209. }
  210. })
  211. let promise = Promise.resolve()
  212. if (image) {
  213. promise = promise.then(() => downloadImage(image))
  214. }
  215. promise = promise.then((imageUrl) => {
  216. return renderCanvas(imageUrl)
  217. })
  218. promise = promise.then((base64Url) => {
  219. setBase64Url(base64Url)
  220. }, (err) => {
  221. this.triggerEvent('error', err)
  222. // console.error(err)
  223. })
  224. return promise
  225. },
  226. updateStyle(zIndex, gapX, width, base64Url) {
  227. const wrapStyle = styleToCssString({
  228. zIndex,
  229. backgroundSize: `${gapX + width}px`,
  230. backgroundImage: base64Url ? `url('${base64Url}')` : 'unset',
  231. })
  232. this.setData({
  233. wrapStyle,
  234. })
  235. },
  236. },
  237. ready() {
  238. const { zIndex, gapX, width, base64Url } = this.data
  239. this.updateStyle(zIndex, gapX, width, base64Url)
  240. this.createCanvasContext(this.data)
  241. },
  242. })