【html2canvas】 text-shadowのバグを要素を重ねまくって解決した話（コピペ可）

# 要旨
html2canvasは複数のtext-shadowを指定すると正常に読み込まないバグを抱えています。それを
強引に
解決できたのでやり方の解説とバグ回避用のJavaScriptを公開しています。コピペしてご利用ください。

# 目次
- <a href="#背景">背景</a>
- <a href="#解決策のアルゴリズム">解説</a>
- <a href="#配布verコピペしてどうぞ">配布用JS（コピペ目的の人向け）</a>
- <a href="#参考文献3年前に私がテラテイルに投げた質問">参考文献</a>

# 背景
<a href="http://html2canvas.hertzen.com" target="_new">html2canvas</a>は<a href="https://hertzen.com">Niklas von Hertzen</a>が開発しているjavascriptライブラリで主にスクリーンショット機能を目的に使用されています。
私は<a href="https://css.black/zimaku" target="_new">CSSで字幕を作成するアプリケーション(自作)</a>の開発に用いたのですが、
`text-shadow`の値を最初の一つしか読み込まない
バグ
仕様にぶつかりました。
アプリケーションの仕様上、妥協するわけには行かなかったので、
`text-shadow`の値の数だけ要素を動的に生成して重ねることで強引に突破しました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">text-shadowの値をRGB-&gt;HEXして&quot;,&quot;でsplitして配列のlength分だけfor文で要素を生成して対象の要素のCSSを完コピさせた上で対象の要素の上にかぶせることでできたんだけど、この手法思いつくまでにクソほど時間かかった。ってかもっとスマートに書きてぇのにこれしかアルゴリズム思いつかんかった。</p>&mdash; LOVESANA (らぶさな) (@LovesanaCSS) <a href="https://twitter.com/LovesanaCSS/status/1147895564534603778?ref_src=twsrc%5Etfw">July 7, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
3年経っても誰も書かなかったので就活アピールも兼ねて書いていきます。<br>
初Qiitaカキコなのでお手柔らかにお願いします。（<S>さっき登録しました</S>）<br>
ご指摘や改善点は歓迎です！




# 問題点の整理
### 環境
- 使用ライブラリ
    - html2canvas 0.4.1
- 動作確認済ブラウザ
    - Safari バージョン14.1.2 (14611.3.10.1.7)
    - Google Chrome バージョン: 104.0.5112.79（Official Build）
- 実行環境
    - MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)
        - macOS Mojave 10.14.6（18G8022）
### 発生している問題
`text-shadow` の値が先頭の一つしか読み込まれません。
詳細は下記のデモで確認してください。
### デモンストレーション（CodePen）
やかましいほど<code>text-shadow</code>の値をつけてみましたが、
生成された<code>&lt;img&gt;</code>は先頭の<code>REbeccapurple</code>しか読み込まれていません。
<p class="codepen" data-height="550" data-default-tab="html,result" data-slug-hash="poLOJLG" data-user="lovesana" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lovesana/pen/poLOJLG">
  html2canvas_text-shadowBUG_demo.html</a> by LOVESANA (<a href="https://codepen.io/lovesana">@lovesana</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


# 解決策のアルゴリズム
### html2canvasをいじって機能を追加する

しんどい

### text-shadowの値の数だけ要素を生成して重ねる（イメージ図）
<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="GRxYgKz" data-user="lovesana" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lovesana/pen/GRxYgKz">
  html2canvas_text-shadowBUG_imaging</a> by LOVESANA (<a href="https://codepen.io/lovesana">@lovesana</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
いけそう、作ろう。



# つくっていく
### まずは`text-shadow`の値を抽出してHEXに変換して配列に入れる
<p class="codepen" data-height="550" data-default-tab="html,result" data-slug-hash="OJvowpB" data-user="lovesana" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lovesana/pen/OJvowpB">
  Untitled</a> by LOVESANA (<a href="https://codepen.io/lovesana">@lovesana</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

できたっぽいです。正常に一般名からHEXに変換できてますね。

### 次に`text-shadow`の値の数だけダミーを生成する。

<p class="codepen" data-height="700" data-default-tab="js,result" data-slug-hash="qBoMMXJ" data-user="lovesana" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lovesana/pen/qBoMMXJ">
  html2canvas_text-shadowBUG_１</a> by LOVESANA (<a href="https://codepen.io/lovesana">@lovesana</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

ちゃんと色ごとに分離してくれました。
（クロマトグラフィーみたい）

生成画像も全色を拾えていますね。

### 最後にダミーを重ね合わせて・・・

<p class="codepen" data-height="700" data-default-tab="js,result" data-slug-hash="jOzeOvO" data-user="lovesana" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lovesana/pen/jOzeOvO">
  html2canvas_text-shadowBUG_2</a> by LOVESANA (<a href="https://codepen.io/lovesana">@lovesana</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

できました！


:::note warn
html2canvasは領域外にfixedするなどして描画領域外に配置された要素を正常に画像化できないのでダミー元のターゲットに重ねるなど工夫が必要になります。どうしても重ねたくない場合は画像化した瞬間にダミー要素を削除するなどの工夫で対策してください。
:::




# 配布ver.コピペしてどうぞ！
<p class="codepen" data-height="600" data-default-tab="js,result" data-slug-hash="ZExMbxp" data-user="lovesana" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/lovesana/pen/ZExMbxp">
  html2canvas_text-shadowFixedBUG_demo.html</a> by LOVESANA (<a href="https://codepen.io/lovesana">@lovesana</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


# 参考文献(3年前に私がテラテイルに投げた質問)
- <a href="https://teratail.com/questions/197770?link=qa_related_pc_sidebar">html2canvasでtext-shadowに複数の値を対応させる方法</a>

# ご質問や連絡先
- <a href="https://twitter.com/LovesanaCSS" target="_new">@LovesanaCSS</a>