GASを使ってパワポカラオケのスライドを自動生成する


パワポカラオケとは?

何も言わず下記の動画を見てください。私はオードリーが大好きです。(すべて「【公式】オードリーさん、ぜひ会ってほしい人がいるんです。」の公式YouTubeチャンネルからです)

...みんなすごいセンスですよね。チャレンジしてみたくなりますよね。

きっかけ

社内でパワポカラオケをやろうと思うのですが、画像をランダムで選んだり、スライドを作ったりするのって自動化できるんじゃないの?

→ できました。

GASでやっていること

  • Googleドライブの images フォルダに画像を適当に入れておく
  • スプレッドシートに必要事項を入れてボタンを押す
  • 指定した枚数の画像がランダムで選ばれたスライドが出来上がる

フォルダ構成

imagesフォルダの中

今回は ぱくたそさんから画像をいただきました。
ぱくたそ の何がスゴイって、サイトの右上に「ガチャ」というボタンがありまして、これを押すと膨大な画像の中から一枚をランダムで表示してくれるんですよね。

…あれ?だったらこのサイトだけでパワポカラオケが成立するんじゃないの?って言っちゃダメ! (>_<)

スプレッドシートの設定

黄色いところに入力して、ボタンを押すだけ。
ボタンを押すと GASの main() が動くように設定してあります。
1.のフォルダは images フォルダで、
3.のフォルダは パワポカラオケジェネレーター フォルダ のことです。

GASの設定

このスプレッドシートのスクリプトエディタに下記を書いてあります。

main
const SLIDE_FOLDER_ID  = "xxxxxxxxxxxxxxx"; // 「パワポカラオケジェネレータ」フォルダのID
const IMAGES_FOLDER_ID = "xxxxxxxxxxxxxxx"; // 「images」フォルダのID

/**
 * スプレッドシートのボタンが押されたらここが起動
 */
function main() {
  const sheet = SpreadsheetApp.getActive().getSheetByName("使い方");
  const imageCount = sheet.getRange("D14").getValue();
  const fileName = sheet.getRange("D15").getValue();
  const theme = sheet.getRange("D13").getValue();

  if(!imageCount || !fileName || !theme) throw new Error("入力に不備があります");

  generateSlide(fileName, theme, imageCount);
}
generateSlide
/**
 * 本体はここ
 */
function generateSlide(fileName, theme, imageCount) {
  // 新規スライド作成
  const presentation = createNewPresentation(fileName);

  // 1ページ目の設定
  setTopSlide(presentation, theme, imageCount);

  // 画像を選ぶ
  const selectedImages = selectImages(imageCount);

  // スライドを追加し、画像を一枚配置する
  for (let i = 0; i < imageCount; i++) {
    // 空白スライドを追加する
    const appendedSlide = presentation.appendSlide(SlidesApp.PredefinedLayout.BLANK);

    // 画像を追加する
    const image = appendedSlide.insertImage(selectedImages[i].getBlob());

    // 画像を中央揃えにする
    const imageWidth = image.getWidth();
    const imageHeight = image.getHeight();
    const pageWidth = presentation.getPageWidth();
    const pageHeight = presentation.getPageHeight();
    const x = (pageWidth / 2) - (imageWidth / 2);
    const y = (pageHeight / 2) - (imageHeight / 2);
    image.setLeft(x).setTop(y);
  }
}
createNewPresentation
/**
 * 新規プレゼンテーションを作成
 * @param {string} fileName
 * @return {object} プレゼンテーションオブジェクト
 */
function createNewPresentation(fileName) {
  // 新規スライドをマイドライブに作る
  const presentation = SlidesApp.create(fileName);
  const presentationId = presentation.getId();

  // 指定したフォルダに移動する
  const file = DriveApp.getFileById(presentationId);
  const folder = DriveApp.getFolderById(SLIDE_FOLDER_ID);
  file.moveTo(folder);

  return presentation;
}
setTopSlide
/**
 * 1ページ目の設定
 * @param {object} presentation プレゼンテーションオブジェクト
 * @param {string} theme パワポカラオケのテーマ
 * @param {number} imageCount 画像の枚数
 */
function setTopSlide(presentation, theme, imageCount) {
  const slide = presentation.getSlides()[0];
  const shapes = slide.getShapes();

  const titleShape = shapes[0];
  const titleTextRange = titleShape.getText();
  titleTextRange.setText(`テーマ: ${theme}`);

  const subTitleShape = shapes[1];
  const subTitleTextRange = subTitleShape.getText();
  subTitleTextRange.setText(`${imageCount}枚のスライドでプレゼンします!`);
}
selectImages
/**
 * imagesフォルダにある画像から指定された枚数の画像をランダムで選ぶ
 * @param {number} imageCount 何枚選ぶか
 * @return {array} file の配列
 */
function selectImages(imageCount) {
  const imageFolder = DriveApp.getFolderById(IMAGES_FOLDER_ID);
  const fileIterator = imageFolder.getFiles();

  // imagesフォルダ内のファイルを配列にする
  const images = [];
  while (fileIterator.hasNext()) {
    images.push(fileIterator.next());
  }

  if (imageCount > images.length) throw new Error("imagesフォルダに入っている画像が不足しています");

  // ランダムで画像を選ぶ
  const selectedImages = [];
  for (let i = 0; i < imageCount; i++) {
    const x = Math.floor(Math.random() * images.length);
    selectedImages.push(images.splice(x, 1)[0]);
  }
  return selectedImages;
}

実行結果

こんな感じでスライドが生成されます。
実際に使う時は、発表者とは別な人がこのスライドを作成して、発表者のプレゼンに合わせてスライド送りをしてくださいね。事前に発表者に見せちゃダメですよ!

感想

GoogleスライドをGASで操作する、を初めてやってみました。
オブジェクトがいっぱいあって大変でした。

SlideApp
 └ Presentation
   └Slide
    └Shape
     └TextRange

という構造のようなのです。

参考