[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fteam-documents\u002F10":3,"knowledge-related-\u002Fknowledge\u002Fteam-documents\u002F10":1744,"knowledge-series-\u002Fknowledge\u002Fteam-documents\u002F10":1770},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":17,"pinned":18,"draft":18,"body":19,"seo":1740,"stem":1741,"id":1742,"extension":1743},"\u002Fknowledge\u002Fteam-documents\u002F10","图片上传前的自定义水印实践","项目需要在图片上传前叠加动态水印。水印内容不是固定文案，而是由多个动态元素组成（如时间、地点、业务字段等）。","团队文档",[12,13,14,15,16],"team","vue","watermark","performance","geo","2021-06-10",false,{"type":20,"value":21,"toc":1730},"minimark",[22,25,29,32,35,48,56,59,66,69,78,92,95,103,106,114,117,119,127,130,133,160,163,945,947,958,967,970,993,995,1701,1703,1706,1727],[23,24,8],"h1",{"id":8},[26,27,28],"h2",{"id":28},"背景",[30,31,9],"p",{},[30,33,34],{},"虽然七牛云支持图片水印能力，但更适合静态水印或固定参数场景。对于“多元素动态组合”场景，如果完全依赖云端参数拼接，通常会遇到：",[36,37,38,42,45],"ul",{},[39,40,41],"li",{},"动态内容表达能力有限；",[39,43,44],{},"需要额外生成并维护水印资源；",[39,46,47],{},"存储与流量成本增加。",[30,49,50,51,55],{},"因此，本方案采用“",[52,53,54],"strong",{},"前端本地合成水印后再上传","”的方式，降低云端处理复杂度与成本。",[30,57,58],{},"水印效果示意：",[30,60,61],{},[62,63],"img",{"alt":64,"src":65},"效果","https:\u002F\u002Fcdn.wangxiaoze.cn\u002Fstatic\u002Fimages\u002F5d43ee33-af6d-420a-afed-8623d0d5e2fd.svg",[67,68],"hr",{},[26,70,72,73,77],{"id":71},"方案一传统-canvas-直接绘制","方案一：传统 ",[74,75,76],"code",{},"canvas"," 直接绘制",[30,79,80,81,83,84,87,88,91],{},"思路是将原图绘制到 ",[74,82,76],{},"，再通过 ",[74,85,86],{},"fillText","\u002F",[74,89,90],{},"drawImage"," 绘制水印元素。",[30,93,94],{},"优点：",[36,96,97,100],{},[39,98,99],{},"依赖少、性能可控；",[39,101,102],{},"输出过程清晰。",[30,104,105],{},"不足：",[36,107,108,111],{},[39,109,110],{},"复杂水印（多行文本、图标、富样式）需要手动计算大量坐标；",[39,112,113],{},"布局变化时维护成本较高。",[30,115,116],{},"对于动态字段较多、样式复杂的场景，不是最优方案。",[67,118],{},[26,120,122,123,126],{"id":121},"方案二使用-html2canvas-组合-dom-后导出","方案二：使用 ",[74,124,125],{},"html2canvas"," 组合 DOM 后导出",[30,128,129],{},"该方案适合“水印样式复杂、元素动态”的场景。",[30,131,132],{},"核心流程：",[134,135,136,139,142,145,154],"ol",{},[39,137,138],{},"在页面中准备一个“离屏渲染容器”（可见区域外，但仍可被渲染）；",[39,140,141],{},"上传前将原图转为对象 URL，并等待图片加载完成；",[39,143,144],{},"将原图与水印 DOM 组合到离屏容器，并设置为原图尺寸；",[39,146,147,148,150,151,153],{},"使用 ",[74,149,125],{}," 将容器转为 ",[74,152,76],{},"；",[39,155,156,159],{},[74,157,158],{},"canvas -> blob -> File","，得到可上传的新文件。",[30,161,162],{},"示例代码：",[164,165,170],"pre",{"className":166,"code":167,"language":168,"meta":169,"style":169},"language-javascript shiki shiki-themes material-theme-lighter github-light github-dark","this.getDom(\"#file-input\").addEventListener(\"change\", function (e) {\n  \u002F\u002F 仅演示单图上传场景\n  const file = e.target.files[0];\n  if (!file) return;\n\n  const tempImg = new Image();\n  tempImg.src = URL.createObjectURL(file);\n\n  tempImg.addEventListener(\"load\", function () {\n    const node = _this.getDom(\"#node\");\n    const imgDom = _this.getDom(\"img\");\n\n    \u002F\u002F 将原图写入离屏容器，并同步尺寸\n    imgDom.setAttribute(\"src\", tempImg.src);\n    node.style.width = `${tempImg.width}px`;\n    node.style.height = `${tempImg.height}px`;\n\n    html2canvas &&\n      html2canvas(node).then(function (canvas) {\n        canvas.toBlob((blob) => {\n          if (!blob) return;\n\n          \u002F\u002F 生成可上传文件\n          const newFile = new File([blob], file.name, { type: file.type });\n          console.log(newFile);\n\n          \u002F\u002F 本地预览\n          const imgSrc = canvas.toDataURL(\"image\u002Fjpeg\", 1);\n          _this.getDom(\".temp-container\").innerHTML = `\n            \u003Cimg\n              class=\"show\"\n              style=\"width:${tempImg.width}px;height:${tempImg.height}px\"\n              src=\"${imgSrc}\"\n              alt=\"\"\n            \u002F>\n          `;\n        });\n      });\n  });\n});\n","javascript","",[74,171,172,239,246,287,310,317,338,367,372,399,430,458,463,469,500,539,571,576,585,616,641,659,664,670,727,747,752,758,794,825,831,837,870,886,892,898,906,916,926,936],{"__ignoreMap":169},[173,174,177,181,185,189,193,197,201,203,206,208,211,213,215,218,220,223,227,230,234,236],"span",{"class":175,"line":176},"line",1,[173,178,180],{"class":179},"s39Yj","this",[173,182,184],{"class":183},"sP7_E",".",[173,186,188],{"class":187},"sGLFI","getDom",[173,190,192],{"class":191},"su5hD","(",[173,194,196],{"class":195},"sjJ54","\"",[173,198,200],{"class":199},"s_sjI","#file-input",[173,202,196],{"class":195},[173,204,205],{"class":191},")",[173,207,184],{"class":183},[173,209,210],{"class":187},"addEventListener",[173,212,192],{"class":191},[173,214,196],{"class":195},[173,216,217],{"class":199},"change",[173,219,196],{"class":195},[173,221,222],{"class":183},",",[173,224,226],{"class":225},"sbsja"," function",[173,228,229],{"class":183}," (",[173,231,233],{"class":232},"s99_P","e",[173,235,205],{"class":183},[173,237,238],{"class":183}," {\n",[173,240,242],{"class":175,"line":241},2,[173,243,245],{"class":244},"sutJx","  \u002F\u002F 仅演示单图上传场景\n",[173,247,249,252,256,260,263,265,268,270,273,277,281,284],{"class":175,"line":248},3,[173,250,251],{"class":225},"  const",[173,253,255],{"class":254},"s_hVV"," file",[173,257,259],{"class":258},"smGrS"," =",[173,261,262],{"class":191}," e",[173,264,184],{"class":183},[173,266,267],{"class":191},"target",[173,269,184],{"class":183},[173,271,272],{"class":191},"files",[173,274,276],{"class":275},"skxfh","[",[173,278,280],{"class":279},"srdBf","0",[173,282,283],{"class":275},"]",[173,285,286],{"class":183},";\n",[173,288,290,294,296,299,302,305,308],{"class":175,"line":289},4,[173,291,293],{"class":292},"sVHd0","  if",[173,295,229],{"class":275},[173,297,298],{"class":258},"!",[173,300,301],{"class":191},"file",[173,303,304],{"class":275},") ",[173,306,307],{"class":292},"return",[173,309,286],{"class":183},[173,311,313],{"class":175,"line":312},5,[173,314,316],{"emptyLinePlaceholder":315},true,"\n",[173,318,320,322,325,327,330,333,336],{"class":175,"line":319},6,[173,321,251],{"class":225},[173,323,324],{"class":254}," tempImg",[173,326,259],{"class":258},[173,328,329],{"class":258}," new",[173,331,332],{"class":187}," Image",[173,334,335],{"class":275},"()",[173,337,286],{"class":183},[173,339,341,344,346,349,351,354,356,359,361,363,365],{"class":175,"line":340},7,[173,342,343],{"class":191},"  tempImg",[173,345,184],{"class":183},[173,347,348],{"class":191},"src",[173,350,259],{"class":258},[173,352,353],{"class":254}," URL",[173,355,184],{"class":183},[173,357,358],{"class":187},"createObjectURL",[173,360,192],{"class":275},[173,362,301],{"class":191},[173,364,205],{"class":275},[173,366,286],{"class":183},[173,368,370],{"class":175,"line":369},8,[173,371,316],{"emptyLinePlaceholder":315},[173,373,375,377,379,381,383,385,388,390,392,394,397],{"class":175,"line":374},9,[173,376,343],{"class":191},[173,378,184],{"class":183},[173,380,210],{"class":187},[173,382,192],{"class":275},[173,384,196],{"class":195},[173,386,387],{"class":199},"load",[173,389,196],{"class":195},[173,391,222],{"class":183},[173,393,226],{"class":225},[173,395,396],{"class":183}," ()",[173,398,238],{"class":183},[173,400,402,405,408,410,413,415,417,419,421,424,426,428],{"class":175,"line":401},10,[173,403,404],{"class":225},"    const",[173,406,407],{"class":254}," node",[173,409,259],{"class":258},[173,411,412],{"class":191}," _this",[173,414,184],{"class":183},[173,416,188],{"class":187},[173,418,192],{"class":275},[173,420,196],{"class":195},[173,422,423],{"class":199},"#node",[173,425,196],{"class":195},[173,427,205],{"class":275},[173,429,286],{"class":183},[173,431,433,435,438,440,442,444,446,448,450,452,454,456],{"class":175,"line":432},11,[173,434,404],{"class":225},[173,436,437],{"class":254}," imgDom",[173,439,259],{"class":258},[173,441,412],{"class":191},[173,443,184],{"class":183},[173,445,188],{"class":187},[173,447,192],{"class":275},[173,449,196],{"class":195},[173,451,62],{"class":199},[173,453,196],{"class":195},[173,455,205],{"class":275},[173,457,286],{"class":183},[173,459,461],{"class":175,"line":460},12,[173,462,316],{"emptyLinePlaceholder":315},[173,464,466],{"class":175,"line":465},13,[173,467,468],{"class":244},"    \u002F\u002F 将原图写入离屏容器，并同步尺寸\n",[173,470,472,475,477,480,482,484,486,488,490,492,494,496,498],{"class":175,"line":471},14,[173,473,474],{"class":191},"    imgDom",[173,476,184],{"class":183},[173,478,479],{"class":187},"setAttribute",[173,481,192],{"class":275},[173,483,196],{"class":195},[173,485,348],{"class":199},[173,487,196],{"class":195},[173,489,222],{"class":183},[173,491,324],{"class":191},[173,493,184],{"class":183},[173,495,348],{"class":191},[173,497,205],{"class":275},[173,499,286],{"class":183},[173,501,503,506,508,511,513,516,518,521,524,526,528,531,534,537],{"class":175,"line":502},15,[173,504,505],{"class":191},"    node",[173,507,184],{"class":183},[173,509,510],{"class":191},"style",[173,512,184],{"class":183},[173,514,515],{"class":191},"width",[173,517,259],{"class":258},[173,519,520],{"class":195}," `${",[173,522,523],{"class":191},"tempImg",[173,525,184],{"class":195},[173,527,515],{"class":191},[173,529,530],{"class":195},"}",[173,532,533],{"class":199},"px",[173,535,536],{"class":195},"`",[173,538,286],{"class":183},[173,540,542,544,546,548,550,553,555,557,559,561,563,565,567,569],{"class":175,"line":541},16,[173,543,505],{"class":191},[173,545,184],{"class":183},[173,547,510],{"class":191},[173,549,184],{"class":183},[173,551,552],{"class":191},"height",[173,554,259],{"class":258},[173,556,520],{"class":195},[173,558,523],{"class":191},[173,560,184],{"class":195},[173,562,552],{"class":191},[173,564,530],{"class":195},[173,566,533],{"class":199},[173,568,536],{"class":195},[173,570,286],{"class":183},[173,572,574],{"class":175,"line":573},17,[173,575,316],{"emptyLinePlaceholder":315},[173,577,579,582],{"class":175,"line":578},18,[173,580,581],{"class":191},"    html2canvas",[173,583,584],{"class":258}," &&\n",[173,586,588,591,593,596,598,600,603,605,608,610,612,614],{"class":175,"line":587},19,[173,589,590],{"class":187},"      html2canvas",[173,592,192],{"class":275},[173,594,595],{"class":191},"node",[173,597,205],{"class":275},[173,599,184],{"class":183},[173,601,602],{"class":187},"then",[173,604,192],{"class":275},[173,606,607],{"class":225},"function",[173,609,229],{"class":183},[173,611,76],{"class":232},[173,613,205],{"class":183},[173,615,238],{"class":183},[173,617,619,622,624,627,629,631,634,636,639],{"class":175,"line":618},20,[173,620,621],{"class":191},"        canvas",[173,623,184],{"class":183},[173,625,626],{"class":187},"toBlob",[173,628,192],{"class":275},[173,630,192],{"class":183},[173,632,633],{"class":232},"blob",[173,635,205],{"class":183},[173,637,638],{"class":225}," =>",[173,640,238],{"class":183},[173,642,644,647,649,651,653,655,657],{"class":175,"line":643},21,[173,645,646],{"class":292},"          if",[173,648,229],{"class":275},[173,650,298],{"class":258},[173,652,633],{"class":191},[173,654,304],{"class":275},[173,656,307],{"class":292},[173,658,286],{"class":183},[173,660,662],{"class":175,"line":661},22,[173,663,316],{"emptyLinePlaceholder":315},[173,665,667],{"class":175,"line":666},23,[173,668,669],{"class":244},"          \u002F\u002F 生成可上传文件\n",[173,671,673,676,679,681,683,686,689,691,693,695,697,699,702,704,707,710,713,715,717,720,723,725],{"class":175,"line":672},24,[173,674,675],{"class":225},"          const",[173,677,678],{"class":254}," newFile",[173,680,259],{"class":258},[173,682,329],{"class":258},[173,684,685],{"class":187}," File",[173,687,688],{"class":275},"([",[173,690,633],{"class":191},[173,692,283],{"class":275},[173,694,222],{"class":183},[173,696,255],{"class":191},[173,698,184],{"class":183},[173,700,701],{"class":191},"name",[173,703,222],{"class":183},[173,705,706],{"class":183}," {",[173,708,709],{"class":275}," type",[173,711,712],{"class":183},":",[173,714,255],{"class":191},[173,716,184],{"class":183},[173,718,719],{"class":191},"type",[173,721,722],{"class":183}," }",[173,724,205],{"class":275},[173,726,286],{"class":183},[173,728,730,733,735,738,740,743,745],{"class":175,"line":729},25,[173,731,732],{"class":191},"          console",[173,734,184],{"class":183},[173,736,737],{"class":187},"log",[173,739,192],{"class":275},[173,741,742],{"class":191},"newFile",[173,744,205],{"class":275},[173,746,286],{"class":183},[173,748,750],{"class":175,"line":749},26,[173,751,316],{"emptyLinePlaceholder":315},[173,753,755],{"class":175,"line":754},27,[173,756,757],{"class":244},"          \u002F\u002F 本地预览\n",[173,759,761,763,766,768,771,773,776,778,780,783,785,787,790,792],{"class":175,"line":760},28,[173,762,675],{"class":225},[173,764,765],{"class":254}," imgSrc",[173,767,259],{"class":258},[173,769,770],{"class":191}," canvas",[173,772,184],{"class":183},[173,774,775],{"class":187},"toDataURL",[173,777,192],{"class":275},[173,779,196],{"class":195},[173,781,782],{"class":199},"image\u002Fjpeg",[173,784,196],{"class":195},[173,786,222],{"class":183},[173,788,789],{"class":279}," 1",[173,791,205],{"class":275},[173,793,286],{"class":183},[173,795,797,800,802,804,806,808,811,813,815,817,820,822],{"class":175,"line":796},29,[173,798,799],{"class":191},"          _this",[173,801,184],{"class":183},[173,803,188],{"class":187},[173,805,192],{"class":275},[173,807,196],{"class":195},[173,809,810],{"class":199},".temp-container",[173,812,196],{"class":195},[173,814,205],{"class":275},[173,816,184],{"class":183},[173,818,819],{"class":191},"innerHTML",[173,821,259],{"class":258},[173,823,824],{"class":195}," `\n",[173,826,828],{"class":175,"line":827},30,[173,829,830],{"class":199},"            \u003Cimg\n",[173,832,834],{"class":175,"line":833},31,[173,835,836],{"class":199},"              class=\"show\"\n",[173,838,840,843,846,848,850,852,854,857,859,861,863,865,867],{"class":175,"line":839},32,[173,841,842],{"class":199},"              style=\"width:",[173,844,845],{"class":195},"${",[173,847,523],{"class":191},[173,849,184],{"class":195},[173,851,515],{"class":191},[173,853,530],{"class":195},[173,855,856],{"class":199},"px;height:",[173,858,845],{"class":195},[173,860,523],{"class":191},[173,862,184],{"class":195},[173,864,552],{"class":191},[173,866,530],{"class":195},[173,868,869],{"class":199},"px\"\n",[173,871,873,876,878,881,883],{"class":175,"line":872},33,[173,874,875],{"class":199},"              src=\"",[173,877,845],{"class":195},[173,879,880],{"class":191},"imgSrc",[173,882,530],{"class":195},[173,884,885],{"class":199},"\"\n",[173,887,889],{"class":175,"line":888},34,[173,890,891],{"class":199},"              alt=\"\"\n",[173,893,895],{"class":175,"line":894},35,[173,896,897],{"class":199},"            \u002F>\n",[173,899,901,904],{"class":175,"line":900},36,[173,902,903],{"class":195},"          `",[173,905,286],{"class":183},[173,907,909,912,914],{"class":175,"line":908},37,[173,910,911],{"class":183},"        }",[173,913,205],{"class":275},[173,915,286],{"class":183},[173,917,919,922,924],{"class":175,"line":918},38,[173,920,921],{"class":183},"      }",[173,923,205],{"class":275},[173,925,286],{"class":183},[173,927,929,932,934],{"class":175,"line":928},39,[173,930,931],{"class":183},"  }",[173,933,205],{"class":275},[173,935,286],{"class":183},[173,937,939,941,943],{"class":175,"line":938},40,[173,940,530],{"class":183},[173,942,205],{"class":191},[173,944,286],{"class":183},[67,946],{},[26,948,950,951,954,955,957],{"id":949},"方案三svg-片段-canvas-合并","方案三：",[74,952,953],{},"SVG"," 片段 + ",[74,956,76],{}," 合并",[30,959,960,961,963,964,966],{},"如果水印结构相对固定，也可以先将水印模板写成 ",[74,962,953],{},"，再与原图绘制到同一个 ",[74,965,76],{}," 中。",[30,968,969],{},"核心步骤：",[134,971,972,978,987,990],{},[39,973,974,975,977],{},"生成水印 ",[74,976,953],{}," 片段；",[39,979,980,981,983,984,153],{},"将 ",[74,982,953],{}," 转为 ",[74,985,986],{},"Image",[39,988,989],{},"先绘制原图，再在指定位置绘制水印图层；",[39,991,992],{},"导出新文件并上传。",[30,994,162],{},[164,996,998],{"className":166,"code":997,"language":168,"meta":169,"style":169},"\u002F\u002F 水印 SVG 片段\nconst data = `\n  \u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"200\" height=\"200\">\n    \u003CforeignObject width=\"100%\" height=\"100%\">\n      \u003Cdiv xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\n        \u003Cdiv style=\"color:red\">这里是水印元素\u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002FforeignObject>\n  \u003C\u002Fsvg>\n`;\n\nconst imgSvg = new Image();\nconst blob = new Blob([data], { type: \"image\u002Fsvg+xml;charset=utf-8\" });\n\nfunction img2Canvas(img) {\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = img.width;\n  canvas.height = img.height;\n  const ctx = canvas.getContext(\"2d\");\n\n  ctx.drawImage(img, 0, 0);\n\n  const svgUrl = URL.createObjectURL(blob);\n  imgSvg.onload = function () {\n    ctx.drawImage(imgSvg, 20, canvas.height - 120);\n    URL.revokeObjectURL(svgUrl);\n  };\n  imgSvg.src = svgUrl;\n\n  return canvas;\n}\n\nthis.getDom(\"#file-input2\").addEventListener(\"change\", function (e) {\n  const file = e.target.files[0];\n  if (!file) return;\n\n  const tempImg = new Image();\n  tempImg.src = URL.createObjectURL(file);\n\n  tempImg.addEventListener(\"load\", () => {\n    img2Canvas(tempImg).toBlob((blob) => {\n      if (!blob) return;\n      const newFile = new File([blob], file.name, { type: file.type });\n      console.log(newFile);\n    });\n  });\n});\n",[74,999,1000,1005,1017,1022,1027,1032,1037,1042,1047,1052,1058,1062,1079,1118,1122,1137,1165,1185,1203,1232,1236,1262,1266,1289,1307,1344,1363,1368,1382,1386,1395,1400,1404,1447,1473,1489,1493,1509,1533,1537,1561,1589,1607,1655,1673,1683,1692],{"__ignoreMap":169},[173,1001,1002],{"class":175,"line":176},[173,1003,1004],{"class":244},"\u002F\u002F 水印 SVG 片段\n",[173,1006,1007,1010,1013,1015],{"class":175,"line":241},[173,1008,1009],{"class":225},"const",[173,1011,1012],{"class":254}," data",[173,1014,259],{"class":258},[173,1016,824],{"class":195},[173,1018,1019],{"class":175,"line":248},[173,1020,1021],{"class":199},"  \u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"200\" height=\"200\">\n",[173,1023,1024],{"class":175,"line":289},[173,1025,1026],{"class":199},"    \u003CforeignObject width=\"100%\" height=\"100%\">\n",[173,1028,1029],{"class":175,"line":312},[173,1030,1031],{"class":199},"      \u003Cdiv xmlns=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml\">\n",[173,1033,1034],{"class":175,"line":319},[173,1035,1036],{"class":199},"        \u003Cdiv style=\"color:red\">这里是水印元素\u003C\u002Fdiv>\n",[173,1038,1039],{"class":175,"line":340},[173,1040,1041],{"class":199},"      \u003C\u002Fdiv>\n",[173,1043,1044],{"class":175,"line":369},[173,1045,1046],{"class":199},"    \u003C\u002FforeignObject>\n",[173,1048,1049],{"class":175,"line":374},[173,1050,1051],{"class":199},"  \u003C\u002Fsvg>\n",[173,1053,1054,1056],{"class":175,"line":401},[173,1055,536],{"class":195},[173,1057,286],{"class":183},[173,1059,1060],{"class":175,"line":432},[173,1061,316],{"emptyLinePlaceholder":315},[173,1063,1064,1066,1069,1071,1073,1075,1077],{"class":175,"line":460},[173,1065,1009],{"class":225},[173,1067,1068],{"class":254}," imgSvg",[173,1070,259],{"class":258},[173,1072,329],{"class":258},[173,1074,332],{"class":187},[173,1076,335],{"class":191},[173,1078,286],{"class":183},[173,1080,1081,1083,1086,1088,1090,1093,1096,1098,1100,1102,1104,1107,1110,1112,1114,1116],{"class":175,"line":465},[173,1082,1009],{"class":225},[173,1084,1085],{"class":254}," blob",[173,1087,259],{"class":258},[173,1089,329],{"class":258},[173,1091,1092],{"class":187}," Blob",[173,1094,1095],{"class":191},"([data]",[173,1097,222],{"class":183},[173,1099,706],{"class":183},[173,1101,709],{"class":275},[173,1103,712],{"class":183},[173,1105,1106],{"class":195}," \"",[173,1108,1109],{"class":199},"image\u002Fsvg+xml;charset=utf-8",[173,1111,196],{"class":195},[173,1113,722],{"class":183},[173,1115,205],{"class":191},[173,1117,286],{"class":183},[173,1119,1120],{"class":175,"line":471},[173,1121,316],{"emptyLinePlaceholder":315},[173,1123,1124,1126,1129,1131,1133,1135],{"class":175,"line":502},[173,1125,607],{"class":225},[173,1127,1128],{"class":187}," img2Canvas",[173,1130,192],{"class":183},[173,1132,62],{"class":232},[173,1134,205],{"class":183},[173,1136,238],{"class":183},[173,1138,1139,1141,1143,1145,1148,1150,1153,1155,1157,1159,1161,1163],{"class":175,"line":541},[173,1140,251],{"class":225},[173,1142,770],{"class":254},[173,1144,259],{"class":258},[173,1146,1147],{"class":191}," document",[173,1149,184],{"class":183},[173,1151,1152],{"class":187},"createElement",[173,1154,192],{"class":275},[173,1156,196],{"class":195},[173,1158,76],{"class":199},[173,1160,196],{"class":195},[173,1162,205],{"class":275},[173,1164,286],{"class":183},[173,1166,1167,1170,1172,1174,1176,1179,1181,1183],{"class":175,"line":573},[173,1168,1169],{"class":191},"  canvas",[173,1171,184],{"class":183},[173,1173,515],{"class":191},[173,1175,259],{"class":258},[173,1177,1178],{"class":191}," img",[173,1180,184],{"class":183},[173,1182,515],{"class":191},[173,1184,286],{"class":183},[173,1186,1187,1189,1191,1193,1195,1197,1199,1201],{"class":175,"line":578},[173,1188,1169],{"class":191},[173,1190,184],{"class":183},[173,1192,552],{"class":191},[173,1194,259],{"class":258},[173,1196,1178],{"class":191},[173,1198,184],{"class":183},[173,1200,552],{"class":191},[173,1202,286],{"class":183},[173,1204,1205,1207,1210,1212,1214,1216,1219,1221,1223,1226,1228,1230],{"class":175,"line":587},[173,1206,251],{"class":225},[173,1208,1209],{"class":254}," ctx",[173,1211,259],{"class":258},[173,1213,770],{"class":191},[173,1215,184],{"class":183},[173,1217,1218],{"class":187},"getContext",[173,1220,192],{"class":275},[173,1222,196],{"class":195},[173,1224,1225],{"class":199},"2d",[173,1227,196],{"class":195},[173,1229,205],{"class":275},[173,1231,286],{"class":183},[173,1233,1234],{"class":175,"line":618},[173,1235,316],{"emptyLinePlaceholder":315},[173,1237,1238,1241,1243,1245,1247,1249,1251,1254,1256,1258,1260],{"class":175,"line":643},[173,1239,1240],{"class":191},"  ctx",[173,1242,184],{"class":183},[173,1244,90],{"class":187},[173,1246,192],{"class":275},[173,1248,62],{"class":191},[173,1250,222],{"class":183},[173,1252,1253],{"class":279}," 0",[173,1255,222],{"class":183},[173,1257,1253],{"class":279},[173,1259,205],{"class":275},[173,1261,286],{"class":183},[173,1263,1264],{"class":175,"line":661},[173,1265,316],{"emptyLinePlaceholder":315},[173,1267,1268,1270,1273,1275,1277,1279,1281,1283,1285,1287],{"class":175,"line":666},[173,1269,251],{"class":225},[173,1271,1272],{"class":254}," svgUrl",[173,1274,259],{"class":258},[173,1276,353],{"class":254},[173,1278,184],{"class":183},[173,1280,358],{"class":187},[173,1282,192],{"class":275},[173,1284,633],{"class":191},[173,1286,205],{"class":275},[173,1288,286],{"class":183},[173,1290,1291,1294,1296,1299,1301,1303,1305],{"class":175,"line":672},[173,1292,1293],{"class":191},"  imgSvg",[173,1295,184],{"class":183},[173,1297,1298],{"class":187},"onload",[173,1300,259],{"class":258},[173,1302,226],{"class":225},[173,1304,396],{"class":183},[173,1306,238],{"class":183},[173,1308,1309,1312,1314,1316,1318,1321,1323,1326,1328,1330,1332,1334,1337,1340,1342],{"class":175,"line":729},[173,1310,1311],{"class":191},"    ctx",[173,1313,184],{"class":183},[173,1315,90],{"class":187},[173,1317,192],{"class":275},[173,1319,1320],{"class":191},"imgSvg",[173,1322,222],{"class":183},[173,1324,1325],{"class":279}," 20",[173,1327,222],{"class":183},[173,1329,770],{"class":191},[173,1331,184],{"class":183},[173,1333,552],{"class":191},[173,1335,1336],{"class":258}," -",[173,1338,1339],{"class":279}," 120",[173,1341,205],{"class":275},[173,1343,286],{"class":183},[173,1345,1346,1349,1351,1354,1356,1359,1361],{"class":175,"line":749},[173,1347,1348],{"class":254},"    URL",[173,1350,184],{"class":183},[173,1352,1353],{"class":187},"revokeObjectURL",[173,1355,192],{"class":275},[173,1357,1358],{"class":191},"svgUrl",[173,1360,205],{"class":275},[173,1362,286],{"class":183},[173,1364,1365],{"class":175,"line":754},[173,1366,1367],{"class":183},"  };\n",[173,1369,1370,1372,1374,1376,1378,1380],{"class":175,"line":760},[173,1371,1293],{"class":191},[173,1373,184],{"class":183},[173,1375,348],{"class":191},[173,1377,259],{"class":258},[173,1379,1272],{"class":191},[173,1381,286],{"class":183},[173,1383,1384],{"class":175,"line":796},[173,1385,316],{"emptyLinePlaceholder":315},[173,1387,1388,1391,1393],{"class":175,"line":827},[173,1389,1390],{"class":292},"  return",[173,1392,770],{"class":191},[173,1394,286],{"class":183},[173,1396,1397],{"class":175,"line":833},[173,1398,1399],{"class":183},"}\n",[173,1401,1402],{"class":175,"line":839},[173,1403,316],{"emptyLinePlaceholder":315},[173,1405,1406,1408,1410,1412,1414,1416,1419,1421,1423,1425,1427,1429,1431,1433,1435,1437,1439,1441,1443,1445],{"class":175,"line":872},[173,1407,180],{"class":179},[173,1409,184],{"class":183},[173,1411,188],{"class":187},[173,1413,192],{"class":191},[173,1415,196],{"class":195},[173,1417,1418],{"class":199},"#file-input2",[173,1420,196],{"class":195},[173,1422,205],{"class":191},[173,1424,184],{"class":183},[173,1426,210],{"class":187},[173,1428,192],{"class":191},[173,1430,196],{"class":195},[173,1432,217],{"class":199},[173,1434,196],{"class":195},[173,1436,222],{"class":183},[173,1438,226],{"class":225},[173,1440,229],{"class":183},[173,1442,233],{"class":232},[173,1444,205],{"class":183},[173,1446,238],{"class":183},[173,1448,1449,1451,1453,1455,1457,1459,1461,1463,1465,1467,1469,1471],{"class":175,"line":888},[173,1450,251],{"class":225},[173,1452,255],{"class":254},[173,1454,259],{"class":258},[173,1456,262],{"class":191},[173,1458,184],{"class":183},[173,1460,267],{"class":191},[173,1462,184],{"class":183},[173,1464,272],{"class":191},[173,1466,276],{"class":275},[173,1468,280],{"class":279},[173,1470,283],{"class":275},[173,1472,286],{"class":183},[173,1474,1475,1477,1479,1481,1483,1485,1487],{"class":175,"line":894},[173,1476,293],{"class":292},[173,1478,229],{"class":275},[173,1480,298],{"class":258},[173,1482,301],{"class":191},[173,1484,304],{"class":275},[173,1486,307],{"class":292},[173,1488,286],{"class":183},[173,1490,1491],{"class":175,"line":900},[173,1492,316],{"emptyLinePlaceholder":315},[173,1494,1495,1497,1499,1501,1503,1505,1507],{"class":175,"line":908},[173,1496,251],{"class":225},[173,1498,324],{"class":254},[173,1500,259],{"class":258},[173,1502,329],{"class":258},[173,1504,332],{"class":187},[173,1506,335],{"class":275},[173,1508,286],{"class":183},[173,1510,1511,1513,1515,1517,1519,1521,1523,1525,1527,1529,1531],{"class":175,"line":918},[173,1512,343],{"class":191},[173,1514,184],{"class":183},[173,1516,348],{"class":191},[173,1518,259],{"class":258},[173,1520,353],{"class":254},[173,1522,184],{"class":183},[173,1524,358],{"class":187},[173,1526,192],{"class":275},[173,1528,301],{"class":191},[173,1530,205],{"class":275},[173,1532,286],{"class":183},[173,1534,1535],{"class":175,"line":928},[173,1536,316],{"emptyLinePlaceholder":315},[173,1538,1539,1541,1543,1545,1547,1549,1551,1553,1555,1557,1559],{"class":175,"line":938},[173,1540,343],{"class":191},[173,1542,184],{"class":183},[173,1544,210],{"class":187},[173,1546,192],{"class":275},[173,1548,196],{"class":195},[173,1550,387],{"class":199},[173,1552,196],{"class":195},[173,1554,222],{"class":183},[173,1556,396],{"class":183},[173,1558,638],{"class":225},[173,1560,238],{"class":183},[173,1562,1564,1567,1569,1571,1573,1575,1577,1579,1581,1583,1585,1587],{"class":175,"line":1563},41,[173,1565,1566],{"class":187},"    img2Canvas",[173,1568,192],{"class":275},[173,1570,523],{"class":191},[173,1572,205],{"class":275},[173,1574,184],{"class":183},[173,1576,626],{"class":187},[173,1578,192],{"class":275},[173,1580,192],{"class":183},[173,1582,633],{"class":232},[173,1584,205],{"class":183},[173,1586,638],{"class":225},[173,1588,238],{"class":183},[173,1590,1592,1595,1597,1599,1601,1603,1605],{"class":175,"line":1591},42,[173,1593,1594],{"class":292},"      if",[173,1596,229],{"class":275},[173,1598,298],{"class":258},[173,1600,633],{"class":191},[173,1602,304],{"class":275},[173,1604,307],{"class":292},[173,1606,286],{"class":183},[173,1608,1610,1613,1615,1617,1619,1621,1623,1625,1627,1629,1631,1633,1635,1637,1639,1641,1643,1645,1647,1649,1651,1653],{"class":175,"line":1609},43,[173,1611,1612],{"class":225},"      const",[173,1614,678],{"class":254},[173,1616,259],{"class":258},[173,1618,329],{"class":258},[173,1620,685],{"class":187},[173,1622,688],{"class":275},[173,1624,633],{"class":191},[173,1626,283],{"class":275},[173,1628,222],{"class":183},[173,1630,255],{"class":191},[173,1632,184],{"class":183},[173,1634,701],{"class":191},[173,1636,222],{"class":183},[173,1638,706],{"class":183},[173,1640,709],{"class":275},[173,1642,712],{"class":183},[173,1644,255],{"class":191},[173,1646,184],{"class":183},[173,1648,719],{"class":191},[173,1650,722],{"class":183},[173,1652,205],{"class":275},[173,1654,286],{"class":183},[173,1656,1658,1661,1663,1665,1667,1669,1671],{"class":175,"line":1657},44,[173,1659,1660],{"class":191},"      console",[173,1662,184],{"class":183},[173,1664,737],{"class":187},[173,1666,192],{"class":275},[173,1668,742],{"class":191},[173,1670,205],{"class":275},[173,1672,286],{"class":183},[173,1674,1676,1679,1681],{"class":175,"line":1675},45,[173,1677,1678],{"class":183},"    }",[173,1680,205],{"class":275},[173,1682,286],{"class":183},[173,1684,1686,1688,1690],{"class":175,"line":1685},46,[173,1687,931],{"class":183},[173,1689,205],{"class":275},[173,1691,286],{"class":183},[173,1693,1695,1697,1699],{"class":175,"line":1694},47,[173,1696,530],{"class":183},[173,1698,205],{"class":191},[173,1700,286],{"class":183},[67,1702],{},[26,1704,1705],{"id":1705},"实施建议",[36,1707,1708,1715,1718,1721],{},[39,1709,1710,1711,1714],{},"图片处理完成后及时调用 ",[74,1712,1713],{},"URL.revokeObjectURL"," 释放对象 URL；",[39,1716,1717],{},"对超大图增加压缩或尺寸限制，避免浏览器内存峰值过高；",[39,1719,1720],{},"优先异步处理并给出上传中状态，避免主线程卡顿；",[39,1722,1723,1724,1726],{},"若涉及跨域图片，请确保资源支持 CORS，否则 ",[74,1725,76],{}," 可能被污染。",[510,1728,1729],{},"html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":169,"searchDepth":241,"depth":241,"links":1731},[1732,1733,1735,1737,1739],{"id":28,"depth":241,"text":28},{"id":71,"depth":241,"text":1734},"方案一：传统 canvas 直接绘制",{"id":121,"depth":241,"text":1736},"方案二：使用 html2canvas 组合 DOM 后导出",{"id":949,"depth":241,"text":1738},"方案三：SVG 片段 + canvas 合并",{"id":1705,"depth":241,"text":1705},{"title":8,"description":9},"knowledge\u002Fteam-documents\u002F10","knowledge\u002Fknowledge\u002Fteam-documents\u002F10.md","md",{"items":1745},[1746,1750,1754,1758,1762,1766],{"path":1747,"title":1748,"description":1749,"category":10,"score":578},"\u002Fknowledge\u002Fteam-documents\u002F02","防篡改水印","在各类管理后台、SaaS 平台或内部系统中，页面水印已经是非常常见的能力：一方面在截图时携带账号、姓名、时间等信息，降低截图外传的风险；另一方面在上传图片或文档预览时，通过水印标记上传者与时间，满足审计和合规需求。",{"path":1751,"title":1752,"description":1753,"category":10,"score":502},"\u002Fknowledge\u002Fteam-documents\u002F19","GridView 宫格加载渲染优化","系统首页 GridView 宫格模块接口耗时不高，但首次进入总耗时接近 22s。复盘耗时分层定位过程、代码层面的瓶颈（约 2000 行、100 个 tab 重复节点）、优化手段与最终指标对比。",{"path":1755,"title":1756,"description":1757,"category":10,"score":502},"\u002Fknowledge\u002Fteam-documents\u002F04","老系统升级与兼容改造实践","公司原有业务系统采用 Node + jQuery 架构，长期运行后暴露出典型遗留系统问题：",{"path":1759,"title":1760,"description":1761,"category":10,"score":460},"\u002Fknowledge\u002Fteam-documents\u002F13","新商家系统性能优化实践","商家系统新版本上线后，团队持续针对构建效率与用户体验做了一轮系统性优化。本文记录核心方案与落地结果，供后续版本复用。",{"path":1763,"title":1764,"description":1765,"category":10,"score":460},"\u002Fknowledge\u002Fteam-documents\u002F03","键盘弹起导致底部被顶起问题（H5 适配）","在移动端 H5 页面中，当用户聚焦 input、textarea 等可编辑元素、软键盘弹出时，常见表现包括：",{"path":1767,"title":1768,"description":1769,"category":10,"score":460},"\u002Fknowledge\u002Fteam-documents\u002F22","企业微信 uni-app H5：OAuth 回退白屏与列表缓存","第三方 BI 报表项目，企业微信内嵌 H5，uni-app 编译，history 模式。主页面 BiLink 同时承担静默授权和列表展示，上线后碰到两个问题：授权完清掉 URL 参数，iOS 侧滑返回还是白屏；列表加了缓存以后，刷新行…",{"series":1771,"title":1772,"items":1773,"index":374,"prev":1847,"next":1848},"team-documents","team documents",[1774,1778,1780,1782,1784,1788,1792,1796,1800,1804,1805,1809,1813,1815,1819,1823,1827,1831,1835,1837,1841,1845],{"path":1775,"title":1776,"date":1777,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F01","执行价格弹窗：表头 \u002F 表体合并与可配置表格","2024-07-27",{"path":1747,"title":1748,"date":1779,"pinned":18},"2024-01-09",{"path":1763,"title":1764,"date":1781,"pinned":18},"2024-09-17",{"path":1755,"title":1756,"date":1783,"pinned":18},"2024-12-12",{"path":1785,"title":1786,"date":1787,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F05","内部插件 \u002F 工具库开发实践","2021-08-19",{"path":1789,"title":1790,"date":1791,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F06","企业微信与小程序工单问题","2025-10-01",{"path":1793,"title":1794,"date":1795,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F07","企业微信群工具打开缓慢原因分析","2020-04-12",{"path":1797,"title":1798,"date":1799,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F08","属性排序需求","2024-06-16",{"path":1801,"title":1802,"date":1803,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F09","同业主重复单提醒规则","2020-06-07",{"path":7,"title":8,"date":17,"pinned":18},{"path":1806,"title":1807,"date":1808,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F11","系统下单（重复单提示框）异常复盘","2023-11-21",{"path":1810,"title":1811,"date":1812,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F12","系统自动更新说明","2025-05-26",{"path":1759,"title":1760,"date":1814,"pinned":18},"2025-04-08",{"path":1816,"title":1817,"date":1818,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F14","选项卡切换与 Loading 状态封装","2019-11-03",{"path":1820,"title":1821,"date":1822,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F15","移动端返回事件触发两次问题复盘","2021-03-09",{"path":1824,"title":1825,"date":1826,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F16","在线协作表格接入实践","2023-10-27",{"path":1828,"title":1829,"date":1830,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F17","支付中转页面升级实践","2022-10-15",{"path":1832,"title":1833,"date":1834,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F18","中英文输入法下搜索触发冲突复盘","2024-09-01",{"path":1751,"title":1752,"date":1836,"pinned":18},"2026-03-17",{"path":1838,"title":1839,"date":1840,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F20","H5 GPS 定位问题说明","2023-08-31",{"path":1842,"title":1843,"date":1844,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F21","vxe-table 行 Hover 联动高亮","2024-08-31",{"path":1767,"title":1768,"date":1846,"pinned":18},"2023-08-17",{"path":1801,"title":1802,"date":1803,"pinned":18},{"path":1806,"title":1807,"date":1808,"pinned":18}]