vap-frame-parser.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * Tencent is pleased to support the open source community by making vap available.
  3. *
  4. * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
  5. *
  6. * Licensed under the MIT License (the "License"); you may not use this file except in
  7. * compliance with the License. You may obtain a copy of the License at
  8. *
  9. * http://opensource.org/licenses/MIT
  10. *
  11. * Unless required by applicable law or agreed to in writing, software distributed under the License is
  12. * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  13. * either express or implied. See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. export default class FrameParser {
  17. constructor(source, headData) {
  18. this.config = source || {};
  19. this.headData = headData;
  20. this.frame = [];
  21. this.textureMap = {}
  22. }
  23. private config;
  24. private headData;
  25. private frame;
  26. private textureMap;
  27. private canvas:HTMLCanvasElement;
  28. private ctx:CanvasRenderingContext2D | null;
  29. private srcData;
  30. async init() {
  31. this.initCanvas();
  32. // 判断是url还是json对象
  33. if(/\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]\.json$/.test(this.config)){
  34. this.config = await this.getConfigBySrc(this.config);
  35. }
  36. await this.parseSrc(this.config);
  37. this.canvas.parentNode.removeChild(this.canvas);
  38. this.frame = this.config.frame || [];
  39. return this;
  40. }
  41. initCanvas() {
  42. const canvas = document.createElement('canvas');
  43. const ctx = canvas.getContext('2d');
  44. canvas.style.display = 'none';
  45. document.body.appendChild(canvas);
  46. this.ctx = ctx;
  47. this.canvas = canvas;
  48. }
  49. loadImg(url:string) {
  50. return new Promise((resolve, reject) => {
  51. // console.log('load img:', url)
  52. const img = new Image();
  53. img.crossOrigin = 'anonymous';
  54. img.onload = function() {
  55. resolve(this);
  56. };
  57. img.onerror = function(e) {
  58. console.error('frame 资源加载失败:' + url);
  59. reject(new Error('frame 资源加载失败:' + url));
  60. };
  61. img.src = url;
  62. })
  63. }
  64. parseSrc(dataJson) {
  65. const src = (this.srcData = {});
  66. return Promise.all(
  67. (dataJson.src || []).map(async item => {
  68. item.img = null;
  69. if (!this.headData[item.srcTag.slice(1, item.srcTag.length - 1)]) {
  70. console.warn(`vap: 融合信息没有传入:${item.srcTag}`);
  71. } else {
  72. if (item.srcType === 'txt') {
  73. if (this.headData['fontStyle'] && !item['fontStyle']) {
  74. item['fontStyle'] = this.headData['fontStyle']
  75. }
  76. item.textStr = item.srcTag.replace(/\[(.*)\]/, ($0, $1) => {
  77. return this.headData[$1];
  78. });
  79. item.img = this.makeTextImg(item);
  80. } else if (item.srcType === 'img') {
  81. item.imgUrl = item.srcTag.replace(/\[(.*)\]/, ($0, $1) => {
  82. return this.headData[$1]
  83. });
  84. try {
  85. item.img = await this.loadImg(item.imgUrl + '?t=' + Date.now());
  86. } catch (e) {}
  87. }
  88. if (item.img) {
  89. src[item.srcId] = item;
  90. }
  91. }
  92. })
  93. )
  94. }
  95. /**
  96. * 下载json文件
  97. * @param jsonUrl json外链
  98. * @returns {Promise}
  99. */
  100. getConfigBySrc(jsonUrl:string) {
  101. return new Promise((resolve, reject) => {
  102. const xhr = new XMLHttpRequest();
  103. xhr.open("GET", jsonUrl, true);
  104. xhr.responseType = "json";
  105. xhr.onload = function() {
  106. if (xhr.status === 200 || xhr.status === 304 && xhr.response) {
  107. const res = xhr.response;
  108. resolve(res);
  109. } else {
  110. reject(new Error("http response invalid" + xhr.status));
  111. }
  112. };
  113. xhr.send();
  114. });
  115. }
  116. /**
  117. * 文字转换图片
  118. * @param item
  119. */
  120. makeTextImg(item) {
  121. const { textStr, w, h, color, style, fontStyle } = item
  122. const ctx = this.ctx;
  123. ctx.canvas.width = w;
  124. ctx.canvas.height = h;
  125. ctx.textBaseline = 'middle';
  126. ctx.textAlign = 'center';
  127. const getFontStyle = function() {
  128. const fontSize = Math.min(w / textStr.length, h - 8); // 需留一定间隙
  129. const font = [`${fontSize}px`, 'Arial'];
  130. if (style === 'b') {
  131. font.unshift('bold');
  132. }
  133. return font.join(' ');
  134. }
  135. if (!fontStyle) {
  136. ctx.font = getFontStyle();
  137. ctx.fillStyle = color;
  138. } else if (typeof fontStyle == 'string') {
  139. ctx.font = fontStyle;
  140. ctx.fillStyle = color;
  141. } else if (typeof fontStyle == 'object') {
  142. ctx.font = fontStyle['font'] || getFontStyle();
  143. ctx.fillStyle = fontStyle['color'] || color;
  144. } else if (typeof fontStyle == 'function') {
  145. ctx.font = getFontStyle();
  146. ctx.fillStyle = color;
  147. fontStyle.call(null, ctx, item);
  148. }
  149. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  150. ctx.fillText(textStr, w / 2, h / 2);
  151. // console.log('frame : ' + textStr, ctx.canvas.toDataURL('image/png'))
  152. return ctx.getImageData(0, 0, w, h);
  153. }
  154. getFrame(frame) {
  155. return this.frame.find(item => {
  156. return item.i === frame;
  157. })
  158. }
  159. }