yuki_2020
@yuki_2020

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

html内のjavascriptで複数拡張子の画像パスを指定したい

解決したいこと

フォルダ内にjpgかpngかどちらかの画像が入っており、それを参照して順番に表示するhtmlを作りたいです。

現在jpgのみで実装したコードはあるのですが、javascriptに対する知識がないため、どうしたらいいかわかりません。

具体的には、

for(let i=0; i<{frames}; i++){{
    const img = new Image();
    img.src = `./{illust_id}_ugoira${{i}}.jpg`;
    images.push(img);

となっている部分を、jpg、pngどちらにも対応させてほしいです。

該当するソースコード

<script>
    const images = [];
    for(let i=0; i<{frames}; i++){{
        const img = new Image();
        img.src = `./{illust_id}_ugoira${{i}}.jpg`;
        images.push(img);
    }}
    const canvas = document.querySelector('#ugoira');
    const context = canvas.getContext('2d');
    let count = 0;
    window.addEventListener('load', function(){{
        setInterval(function(){{
            context.clearRect(0, 0, canvas.width, canvas.height);
            context.drawImage(images[count], 0, 0);
            count++;
            if(count>={frames}) count=0;
        }}, {delay});
    }});
</script>

プログラム内の{}で囲われた変数(例えばdelayなど)は後からpythonでフォーマットしています。

0

3Answer

Python で画像のパスが取得できる状況なら、リストを作って JSON 文字列化して変数で渡してやるといいと思います。

import glob
import json

paths = []

for i in range(frames):
    path = glob.glob(f"./{illust_id}_ugoira{i}.*")[0]
    paths.append(path)

paths_json = json.dumps(paths)
// paths_json の値は JS の配列リテラルとしても解釈できる文字列なので
// そのまま埋め込める。
const paths = {paths_json};

const images = paths.map(path => {{
  const image = new Image();
  image.src = path;
  return image;
}});
1Like

JavaScriptで完結させたいならばonerrorとPromiseを組み合わせるのはどうでしょうか。

const promises = [];
for (let i=0; i<{frames}; i++) {{
  promises.push(new Promise((resolve, reject) => {{
    const img = new Image();
    img.onerror = () => {{
      img.onerror = reject; // pngも読み込めない場合はreject
      img.src = `./{illust_id}_ugoira${{i}}.png`;
    }};
    img.onload = resolve;
    img.src = `./{illust_id}_ugoira${{i}}.jpg`;
  }}));
}}
Promise.all(promises).then(images => {{
  const canvas = document.querySelector('#ugoira');
  const context = canvas.getContext('2d');
  let count = 0;
  setInterval(() => {{
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(images[count], 0, 0);
    count++;
    if(count >= images.length) count = 0;
  }}, {delay});
}});

errorになった画像を除いて表示するならばPromise.all()の代わりにPromise.allSettled()を使うとよさそうです。

Promise.allSettled(promises).then(results => {{
  const images = results.filter(r => r.status === 'fulfilled').map(r => r.value);
  // 以下同じ
}});
1Like

@uasiさん、@_y_sさん

迅速な回答ありがとうございます。

私はpythonメインということもあり、今後の拡張性のためにも@uasiさんのコードを使用しようと思います。

しかし@_y_sさんのコードからもいろいろ学べました。
本当にありがとうございます。

なお、@uasiさんのコードのjsonに変換する部分は、
私のプログラムでは特定の画像しか入っていないので、

.py
#jpg以外の画像あるのかわからない
frames = glob.glob(f'{dir_name}/*.jpg')
frames += glob.glob(f'{dir_name}/*.jpeg')
frames += glob.glob(f'{dir_name}/*.png')

#https://note.nkmk.me/python-sort-num-str/
frames.sort(key=lambda s: int(re.findall(r'\d+', s)[-1]))

paths_json = json.dumps(frames)

といった形で、forで回さずglobでとってきたリストをうまくソートしました。

回答いただき、本当にありがとうございました。

1Like

Your answer might help someone💌