Gemini :スライドプログラム

2025/12/27

概要

Google Gemini 3のプログラミングの使い勝手を簡単な課題に対してどれだけ反応するか。

当サイトのトップページに設置しているJavascript製スライドの機能変更をプロンプトしてみた。

json ,html, css , Javascript

仕様 : 表示する画像をフォルダーに格納し別のpythonプログラムで画像名のリストをjsonファイルに作成しておく。これをJavascriptでランダムにスライド表示させる。

jsonファイル

別プログラムでjsonファイルを作りtopimagesフォルダーに置く。jsonファイルの画像ファイル名の後にブランク挟んでコメントは手動で追加する。

images.json [ "topimages/wintera_1.png", "topimages/wintera_2.png", "topimages/wintera_3.png", "topimages/wintera_4.png", "topimages/wintera_5.png", "topimages/wintera_6.png", "topimages/wintera_7.png", "topimages/wintera_8.png", "topimages/wintera_9.png", "topimages/アイスホテル.png アイスホテル(スウェーデン)", "topimages/アラスカの氷河.png アラスカの氷河(アメリカ)", ............................................................................ "topimages/金閣寺.png 金閣寺(日本)" ]

html

.............................................. <div id="slideshow_container"> <div id="slideshow"> <img src="" class="active" alt="slideshow image"> </div> <p>Global Winterscapes</p> </div> ......................................

CSS

/* slide 2  2026.02.04 */
#slideshow_container {
}
#slideshow_container p {
  text-align:right;
  color:white;
  font-size:1.2rem;
  padding:0px;
  margin:0px 5px 2px 0px;
  font-family: "Monotype Corsiva", "Apple Chancery", "Lucida Calligraphy", "Palatino Linotype", "Dancing Script", cursive;
}
#slideshow {
  margin:2px auto;
  position: relative;
  width: 80%;
  aspect-ratio: 16 / 9;
/*  height: 400px;  */
  overflow: hidden; /* 画像がはみ出さないように */
}
#slideshow img {
  position: absolute;
  top: 0;
  left: 0;  
  width: 100%;       /* 横幅を枠いっぱいにする */
  height: 100%;      /* 高さを枠いっぱいにする */
  object-fit: cover; /* 枠に合わせてトリミング(隙間なく埋める) */
  object-position: center center; /* 画像の中心を基準にする */
  opacity: 0;
  transition: opacity 2s ease-in-out, transform 12s linear;
  will-change: opacity, transform;
}
/* 表示状態(JSでこのクラスをつける) */
#slideshow img.active {
  opacity: 1;
}
/* コメントのスタイル 2026.0204 */
.slide-text {
  position: absolute; /* 画像の上に重ねる */
  bottom: 10px;       /* 下からの位置 */
  left: 0;
  width: 100%;
  text-align: center; /* 中央寄せ */
  color: #fff;        /* 白文字 */
  text-shadow: 1px 1px 3px rgba(0,0,0,0.8); /* 読みやすく影をつける */
  font-size: 1.0rem;
  z-index: 1000;    /* 画像より手前に表示  10 */
  /* 文字を見やすくする装飾 */
  color: #ffffff;       /* 白文字 */
  font-size: 1.0rem;    /* 文字サイズ */
  font-weight: bold;    /* 太字 */
  
  /* 白文字が背景に溶け込まないように黒い影をつける */
  text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.8); 
  
  /* フェードイン・アウトのアニメーション */
  opacity: 0;
  transition: opacity 2s ease;
  
  /* 文字選択を防止(スライドショーの邪魔にならないように) */
  pointer-events: none;
}

/* 表示用クラス */
.slide-text.active {
  opacity: 1;
}

/*-------------- mobile responsive -----iphone6s --------------------*/
@media screen and  (max-width: 480px) { 
#slideshow {
  margin:2px auto;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
}

}

/* === アニメーションのバリエーション === */
/* パターンA: ズームイン */
.effect-zoom-in {
  transform: scale(1.0);
}
.effect-zoom-in.active {
  transform: scale(1.15); /* 1.15倍までゆっくり拡大 */
}

/* パターンB: ズームアウト */
.effect-zoom-out {
  transform: scale(1.15);
}
.effect-zoom-out.active {
  transform: scale(1.0);
}

/* パターンC: 左へ少しスライド */
.effect-slide-left {
  transform: translateX(50px);
}
.effect-slide-left.active {
  transform: translateX(0);
}

/* パターンD: ただのフェード(動きなし) */
.effect-fade {
  transform: scale(1.0);
}/*-------------- mobile responsive -----iphone6s --------------------*/
@media screen and  (max-width: 480px) { 
#slideshow {
  margin:2px auto;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
}

}

/* === アニメーションのバリエーション === */
/* パターンA: ズームイン */
.effect-zoom-in {
  transform: scale(1.0);
}
.effect-zoom-in.active {
  transform: scale(1.15); /* 1.15倍までゆっくり拡大 */
}

/* パターンB: ズームアウト */
.effect-zoom-out {
  transform: scale(1.15);
}
.effect-zoom-out.active {
  transform: scale(1.0);
}

/* パターンC: 左へ少しスライド */
.effect-slide-left {
  transform: translateX(50px);
}
.effect-slide-left.active {
  transform: translateX(0);
}

/* パターンD: ただのフェード(動きなし) */
.effect-fade {
  transform: scale(1.0);
}

Javascript

// 2026.02.04 fetch('topimages/images.json') .then(response => response.json()) .then(images => { const slideshow = document.getElementById('slideshow'); const initialImg = slideshow.querySelector('img'); const effects = ['effect-zoom-in', 'effect-zoom-out', 'effect-slide-left', 'effect-fade']; // ★文字列から「パス」と「コメント」を分離する関数 function parseImageData(str) { // 最初の「半角スペース」の位置を探す const splitIndex = str.indexOf(' '); if (splitIndex !== -1) { // スペースが見つかったら、そこで分割 return { src: str.substring(0, splitIndex), // 前半: ファイルパス comment: str.substring(splitIndex + 1) // 後半: コメント }; } else { // スペースがない(コメントがない)場合 return { src: str, comment: '' }; } } // --- 初期画像の処理 --- if (initialImg && images.length > 0) { const randomStr = images[Math.floor(Math.random() * images.length)]; const data = parseImageData(randomStr); // 分離処理を実行 initialImg.src = data.src; initialImg.classList.add('active', 'effect-zoom-in'); // 初期コメント表示 if (data.comment) { const initialCaption = document.createElement('div'); initialCaption.classList.add('slide-text', 'active'); initialCaption.textContent = data.comment; slideshow.appendChild(initialCaption); } } function changeImage() { const randomIndex = Math.floor(Math.random() * images.length); const randomStr = images[randomIndex]; const data = parseImageData(randomStr); // 分離処理を実行 // ★ここにデバッグ用のログを追加してください★ console.log('読み込んだデータ:', data); // 画像要素の作成 const newImg = document.createElement('img'); newImg.src = data.src; newImg.alt = "slideshow image"; const randomEffect = effects[Math.floor(Math.random() * effects.length)]; newImg.classList.add(randomEffect); slideshow.appendChild(newImg); // コメント要素の作成(コメントがある場合のみ) let newCaption = null; if (data.comment) { newCaption = document.createElement('div'); newCaption.classList.add('slide-text'); newCaption.textContent = data.comment; slideshow.appendChild(newCaption); // ★この行を追加して、コンソールを確認してください console.log('★HTMLにコメントを追加しました:', newCaption); } // フェードイン開始 requestAnimationFrame(() => { requestAnimationFrame(() => { newImg.classList.add('active'); if (newCaption) { newCaption.classList.add('active'); } }); }); // 古い要素の削除 setTimeout(() => { const oldImgs = slideshow.querySelectorAll('img'); const oldCaptions = slideshow.querySelectorAll('.slide-text'); if (oldImgs.length > 1) { for (let i = 0; i < oldImgs.length - 1; i++) { oldImgs[i].remove(); } } // 表示中の新しいコメント以外を削除 oldCaptions.forEach(caption => { if (caption !== newCaption) { caption.remove(); } }); }, 2500); } setInterval(changeImage, 12000); }) .catch(error => { console.error('画像リスト読み込みエラー:', error); });

コードの詳細説明

JavascriptのコードをJavascript初心者レベルで詳しく説明して。

完成したコードは、プログラミングの「基本」と「ちょっとした応用」が詰まった、とても勉強になる内容です。 全体の流れを、「料理を作るレストラン」に例えながら、初心者の方にもわかるようにブロックごとに解説します。 全体の流れ このプログラムは、大きく分けて3つのステップで動いています。 仕入れ: images.json(メニュー表)を読み込む。 下準備: 読み込んだ文字(例: "写真.jpg コメント")を、「画像」と「コメント」に包丁で切り分ける。 提供: 時間ごとに新しい画像とコメントを作って、画面(スライドショー)に出す。 ブロックごとの詳しい解説 1. データの仕入れ(fetch) JavaScript fetch('topimages/images.json') .then(response => response.json()) .then(images => { // ... ここに全ての処理が入っています }) fetch (フェッチ): 「取ってくる」という意味です。「images.json を取ってきて!」と命令しています。 .then (ドット・ゼン): 「そのあとに」という意味です。「取ってきたら(then)、それをプログラムで使えるデータ形式に変換して(json)、そのあとに(then)、images という名前で中身を使ってね」という流れ作業です。 2. 下準備:文字を切り分ける機能(parseImageData) ここが今回の一番のポイントです! JavaScript // ★文字列から「パス」と「コメント」を分離する関数(道具) function parseImageData(str) { // 最初の「半角スペース」が何番目にあるか探す const splitIndex = str.indexOf(' '); if (splitIndex !== -1) { // スペースが見つかったら return { src: str.substring(0, splitIndex), // 前半: 画像のパス comment: str.substring(splitIndex + 1) // 後半: コメント }; } else { // スペースがない(コメントがない)場合 return { src: str, comment: '' }; } } 役割: JSONに書かれた "topimages/a.jpg コメント" という1本の長いテープのような文字を、半角スペースを境目にしてチョキンと2つに切る「専用のハサミ」を作っています。 indexOf(' '): 「スペースはどこ?」と探します。見つかればその場所の番号を、見つからなければ -1 を返します。 substring: 指定した場所で文字を切り取ります。 3. 最初の1枚を表示する ページを開いた瞬間、まだタイマーは動いていませんが、画面が真っ白だと寂しいので、とりあえず1枚目を手動で出します。 JavaScript if (initialImg && images.length > 0) { // ランダムに1つ選ぶ const randomStr = images[Math.floor(Math.random() * images.length)]; // さっき作ったハサミで切って、中身を取り出す const data = parseImageData(randomStr); // もともとHTMLにあったimgタグの中身を書き換える initialImg.src = data.src; // コメントがあれば、新しい箱(div)を作って表示する if (data.comment) { // ...(要素を作る処理) } } 4. メインの動き:画像を切り替える(changeImage) ここが12秒ごとに呼び出される「メインの仕事」です。 JavaScript function changeImage() { // 1. 次の画像をランダムに選んで、ハサミで切る const randomIndex = Math.floor(Math.random() * images.length); const data = parseImageData(images[randomIndex]); // 2. 新しい タグ(部品)を作る const newImg = document.createElement('img'); newImg.src = data.src; // 画像のアドレスを入れる // 3. エフェクト(ズームなど)をランダムにつける const randomEffect = effects[Math.floor(Math.random() * effects.length)]; newImg.classList.add(randomEffect); // 4. 画面(slideshow)に追加する slideshow.appendChild(newImg); // 5. コメントがあれば、コメント用の部品も作る let newCaption = null; if (data.comment) { newCaption = document.createElement('div'); newCaption.classList.add('slide-text'); newCaption.textContent = data.comment; slideshow.appendChild(newCaption); } // ...(続く) document.createElement: 画面上にない新しい部品(タグ)を魔法のように生み出します。 appendChild: 生み出した部品を、スライドショーの枠の中に「ポン」と置きます。この時点ではまだ透明(opacity: 0)です。 5. フェードイン(じわっと表示) JavaScript // ブラウザの準備が整ったら... requestAnimationFrame(() => { requestAnimationFrame(() => { // 'active' クラスをつける(CSSで opacity: 1 になる) newImg.classList.add('active'); if (newCaption) { newCaption.classList.add('active'); } }); }); なぜ requestAnimationFrame を使うの?: 部品を置いてすぐに active クラスをつけると、ブラウザが「あわわ」と焦ってしまい、アニメーション(じわっと表示)せずに一瞬でパッと表示されてしまうことがあります。 「一呼吸おいてからクラスをつけてね」とお願いすることで、なめらかにフェードインさせています。 6. お片付け(古い画像の削除) JavaScript // 2.5秒(2500ミリ秒)待ってから掃除する setTimeout(() => { // ...(古い画像を消す処理) // コメントも掃除する oldCaptions.forEach(caption => { // 「今表示したばかりの新しいコメント」以外は全部捨てる if (caption !== newCaption) { caption.remove(); } }); }, 2500); setTimeout: 「○ミリ秒後にこれをやってね」というタイマーです。 なぜ消すの?: 消さないと、裏側に古い画像やコメントが何百枚も積み重なってしまい、ブラウザが重くなってしまいます。 ポイント: 新しい画像が完全に表示され終わってから(フェードインが終わってから)、古いものを消しています。そうしないと、一瞬背景が見えてチカチカしてしまうからです。 まとめ このコードは、 「文字を分解する」→「新しい部品を作る」→「画面に乗せる」→「じわっと表示する」→「古いのを捨てる」 というサイクルを、12秒ごとに繰り返しているだけです。

実行サンプル

実行サンプル

感想

Javascriptコードが非常に簡潔に組まれており特にコメントが適切。第3者が解りやすいコーディングは優れたプログラミングに間違いない。

戻る
.