【Node.js/sharp】縦に長い画像を分割する

公開:2020/10/25
更新:2020/10/25
3 min読了の目安(約3300字TECH技術記事

概要

Node.jsを使用して、縦に長い画像を分割する。

覚え書き

sharp

lovell/sharp: High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. Uses the libvips library.

Node.js用の画像編集ライブラリ。

始めは画像の加工をcanvasでゴリゴリやろうと思ったが、画像周りの扱い方を検索しているうちにこのライブラリを発見した。以前も見つけたことがあり「名前の関係でググラビリティが低い」といわれていたのを思い出したので、あえてこのライブラリを使ってみることにした🤔

環境

sharp 0.26.2

実装

基本形

縦に長い画像を正方形に分割している。

(async () => {
  const sharp = require('sharp')
  const base = await sharp('input/image.png')

  const {
    width: baseWidth,
    height: baseHeight
  } = await base.metadata()

  const n = Math.ceil(baseHeight / baseWidth)

  for (let i = 0; i < n; i++) {
    const top = baseWidth * i
    const height = Math.min(baseHeight - top, baseWidth)

    await base.clone().extract({
      left: 0,
      top,
      width: baseWidth,
      height
    }).toFile(`output/image-${i + 1}.png`)
  }
})()

画像のサイズを揃える

縦長画像が分割サイズの倍数ピッタリに作られていないと最後の画像の高さが可変になるので、extendで余白を足す。

for (let i = 0; i < n; i++) {
    // ...

    await base.clone().extract({
      left: 0,
      top,
      width: baseWidth,
      height
    }).extend({
      top: 0,
      bottom: extendBottom,
      left: 0,
      right: 0,
      background: '#fff'
    }).toFile(`output/image-${i + 1}.png`)
  }
全体コード
(async () => {
  const sharp = require('sharp')

  const base = await sharp('input/image.png')

  const {
    width: baseWidth,
    height: baseHeight
  } = await base.metadata()

  const n = Math.ceil(baseHeight / baseWidth)

  for (let i = 0; i < n; i++) {
    const top = baseWidth * i
    const height = Math.min(baseHeight - top, baseWidth)
    const extendBottom = baseWidth - height

    await base.clone().extract({
      left: 0,
      top,
      width: baseWidth,
      height
    }).extend({
      top: 0,
      bottom: extendBottom,
      left: 0,
      right: 0,
      background: '#fff'
    }).toFile(`output/image-${i + 1}.png`)
  }
})()

前の画像と被らせる

2枚目以降の画像で、上の部分を前の画像と被らせることにより繋がりを分かりやすくする(アニメでCM明けにCM直前のシーンを流すようなアレ)。

const offsetY = 64
const n = Math.ceil((baseHeight + Math.floor(baseHeight / baseWidth) * offsetY) / baseWidth)

for (let i = 0; i < n; i++) {
  const top = baseWidth * i - offsetY * i
  // ...
}
全体コード
(async () => {
  const sharp = require('sharp')

  const base = await sharp('input/image.png')

  const {
    width: baseWidth,
    height: baseHeight
  } = await base.metadata()

  const offsetY = 64
  const n = Math.ceil((baseHeight + Math.floor(baseHeight / baseWidth) * offsetY) / baseWidth)

  for (let i = 0; i < n; i++) {
    const top = baseWidth * i - offsetY * i
    const height = Math.min(baseHeight - top, baseWidth)
    const extendBottom = baseWidth - height

    await base.clone().extract({
      left: 0,
      top,
      width: baseWidth,
      height
    }).extend({
      top: 0,
      bottom: extendBottom,
      left: 0,
      right: 0,
      background: '#fff'
    }).toFile(`output/image-${i + 1}.png`)
  }
})()

参考