Node.js - create sprite sheet for JavaScript
const glob = require('glob')
const mkdirp = require('mkdirp')
const { writeFile } = require('fs')
const { promisify } = require('util')
const { relative, dirname, basename, extname, join } = require('path')
const Spritesmith = require('spritesmith')
const imagemin = require('imagemin')
const pngquant = require('imagemin-pngquant')
const mozjpeg = require('imagemin-mozjpeg')
const gifsicle = require('imagemin-gifsicle')
const plugins = {
png: pngquant({
quality: [0.5, 0.6], // 0 ~ 1
speed: 1
}),
jpg: mozjpeg({
quality: 80 // 0 ~ 100
}),
gif: gifsicle({
optimizationLevel: 3 // 1 ~ 3
})
}
const options = {
algorithmOpts: { sort: false }
}
const writeFileAsync = promisify(writeFile)
const globAsync = promisify(glob)
const mkdirpAsync = promisify(mkdirp)
const imgSrcDir = 'src/assets/sprite-sheet'
const imgDistDir = 'src/assets/images'
const imgPathDir = '/assets/images'
const jsDistPath = 'src/assets/js/data/sprite-sheet.js'
;(async () => {
console.log('[sprite-sheet] start')
const files = await globAsync(join(imgSrcDir, `**/*.+(png|jpg|gif)`))
const pathMap = groupBy(files)
const spritehashs = await Promise.all(
(() => {
const _promises = []
pathMap.forEach((paths, key) => {
_promises.push(spritesmith(key, paths))
})
return _promises
})()
)
const _str = `export const spriteSheet = ${JSON.stringify(spritehashs)};`
await mkdirpAsync(dirname(jsDistPath))
await writeFileAsync(jsDistPath, _str)
console.log(`[sprite-sheet] create ${jsDistPath}`)
console.log('[sprite-sheet] end')
})()
const spritesmith = (key, paths) => {
return new Promise((resolve, reject) => {
Spritesmith.run(
Object.assign(options, {
src: paths
}),
(err, result) => {
if (err) return reject(err)
const { coordinates, properties, image } = result
const [name, ext] = key.split('--')
const _fileName = `${name}.${ext}`
const _dest = join(imgDistDir, _fileName)
const _data = {
id: name,
path: join(imgPathDir, _fileName),
sheet: {}
}
;(async () => {
const _buf = await imagemin
.buffer(new Buffer(image, 'base64'), {
plugins: [plugins[ext]]
})
.catch((err) => {
if (err) {
console.log(err)
return
}
})
await mkdirpAsync(dirname(_dest))
await writeFileAsync(_dest, _buf)
console.log(`[sprite-sheet] created image ${_dest}`)
for (const [path, data] of Object.entries(coordinates)) {
const _name = basename(path, extname(path))
const _width = data.width
const _height = data.height
const _maxRatio = maxRatio(_width, _height)
const _minRatio = minRatio(_width, _height)
_data.sheet[_name] = {
x: data.x / properties.width,
y: data.y / properties.height,
width: _width / properties.width,
height: _height / properties.height
widthRatio: _width / _height,
heightRatio: _height / _width,
maxRatio: _maxRatio.ratio,
maxWidthRatio: _maxRatio.width,
maxHeightRatio: _maxRatio.height,
minRatio: _minRatio.ratio,
minWidthRatio: _minRatio.width,
minHeightRatio: _minRatio.height
}
}
resolve(_data)
})()
}
)
}).catch((err) => {
console.error(err)
})
}
const groupBy = (paths) => {
return paths.reduce((memo, path) => {
const _dir = dirname(relative(imgSrcDir, path))
if (!memo.has(_dir)) {
memo.set(_dir, [])
}
memo.get(_dir).push(path)
return memo
}, new Map())
}
const maxRatio = (w, h) => {
const _maxRatio = Math.max(w / h, h / w)
return {
raito: _maxRatio,
width: w > h ? _maxRatio : 1,
height: h > w ? _maxRatio : 1
}
}
const minRatio = (w, h) => {
const _minRatio = Math.min(w / h, h / w)
return {
raito: _minRatio,
width: w > h ? 1 : _minRatio,
height: h > w ? 1 : _minRatio
}
}