habitica-self-host/gulp/gulp-sprites.js

165 lines
5.3 KiB
JavaScript
Raw Normal View History

2015-09-12 00:59:06 +00:00
import gulp from 'gulp';
import imagemin from 'gulp-imagemin';
import spritesmith from 'gulp.spritesmith';
2015-11-08 01:09:51 +00:00
import clean from 'rimraf';
2015-09-12 00:59:06 +00:00
import sizeOf from 'image-size';
2015-09-13 01:43:04 +00:00
import mergeStream from 'merge-stream';
2019-10-08 14:57:10 +00:00
import { basename } from 'path';
import { sync } from 'glob';
import { each } from 'lodash';
import vinylBuffer from 'vinyl-buffer';
2015-09-12 00:59:06 +00:00
// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
const IMG_DIST_PATH = 'website/client/src/assets/images/sprites/';
const CSS_DIST_PATH = 'website/client/src/assets/css/sprites/';
function checkForSpecialTreatment (name) {
2019-10-08 14:57:10 +00:00
const regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame|^eyewear_special_\w+HalfMoon/;
return name.match(regex) || name === 'head_0';
}
2015-09-12 00:59:06 +00:00
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
2015-09-12 00:59:06 +00:00
2019-10-08 14:57:10 +00:00
const requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
2019-10-08 14:57:10 +00:00
const newWidth = dims.width < 90 ? 90 : dims.width;
const newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
2015-09-12 15:24:06 +00:00
let padding = 0;
2015-09-12 00:59:06 +00:00
if (addPadding) {
padding = dims.width * 8 + dims.height * 8;
}
2015-09-12 00:59:06 +00:00
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
2015-09-12 00:59:06 +00:00
2019-10-08 14:57:10 +00:00
const totalPixelSize = dims.width * dims.height + padding;
2015-09-12 00:59:06 +00:00
return totalPixelSize;
}
2015-09-12 00:59:06 +00:00
function calculateSpritesheetsSrcIndicies (src) {
let totalPixels = 0;
2019-10-08 14:57:10 +00:00
const slices = [0];
each(src, (img, index) => {
2019-10-08 14:57:10 +00:00
const imageSize = calculateImgDimensions(img, true);
totalPixels += imageSize;
if (totalPixels > MAX_SPRITESHEET_SIZE) {
slices.push(index - 1);
totalPixels = imageSize;
2015-09-12 00:59:06 +00:00
}
});
return slices;
}
function cssVarMap (sprite) {
2019-10-08 14:57:10 +00:00
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class,
// which works as a 60x60 image pointing at the proper part of the 90x90 sprite.
// We set up the custom info here, and the template makes use of it.
2019-10-08 14:57:10 +00:00
const requiresSpecialTreatment = checkForSpecialTreatment(sprite.name);
if (requiresSpecialTreatment) {
sprite.custom = {
px: {
2019-10-08 14:57:10 +00:00
offsetX: `-${sprite.x + 25}px`,
offsetY: `-${sprite.y + 15}px`,
width: '60px',
height: '60px',
},
};
2015-09-12 00:59:06 +00:00
}
2019-10-08 14:57:10 +00:00
if (sprite.name.indexOf('shirt') !== -1) sprite.custom.px.offsetY = `-${sprite.y + 35}px`; // even more for shirts
if (sprite.name.indexOf('hair_base') !== -1) {
2019-10-08 14:57:10 +00:00
const styleArray = sprite.name.split('_').slice(2, 3);
if (Number(styleArray[0]) > 14) sprite.custom.px.offsetY = `-${sprite.y}px`; // don't crop updos
}
}
2015-09-12 00:59:06 +00:00
function createSpritesStream (name, src) {
2019-10-08 14:57:10 +00:00
const spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
const stream = mergeStream();
2015-09-12 16:09:29 +00:00
each(spritesheetSliceIndicies, (start, index) => {
2019-10-08 14:57:10 +00:00
const slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
2015-09-13 01:43:04 +00:00
2019-10-08 14:57:10 +00:00
const spriteData = gulp.src(slicedSrc)
2015-09-13 01:43:04 +00:00
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap,
2015-09-13 01:43:04 +00:00
}));
2019-10-08 14:57:10 +00:00
const imgStream = spriteData.img
.pipe(vinylBuffer())
2015-09-13 01:43:04 +00:00
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
2015-09-13 01:43:04 +00:00
2019-10-08 14:57:10 +00:00
const cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
2015-09-13 01:43:04 +00:00
stream.add(imgStream);
stream.add(cssStream);
2015-09-12 16:09:29 +00:00
});
2015-09-13 01:43:04 +00:00
return stream;
2015-09-12 16:09:29 +00:00
}
gulp.task('sprites:main', () => {
2019-10-08 14:57:10 +00:00
const mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
2015-09-12 00:59:06 +00:00
gulp.task('sprites:largeSprites', () => {
2019-10-08 14:57:10 +00:00
const largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
2019-10-08 14:57:10 +00:00
gulp.task('sprites:clean', done => {
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', gulp.series('sprites:main', done => {
console.log('Verifying that images do not exceed max dimensions'); // eslint-disable-line no-console
let numberOfSheetsThatAreTooBig = 0;
2015-09-12 00:59:06 +00:00
2019-10-08 14:57:10 +00:00
const distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
2015-09-12 00:59:06 +00:00
2019-10-08 14:57:10 +00:00
each(distSpritesheets, img => {
const spriteSize = calculateImgDimensions(img);
2015-09-12 00:59:06 +00:00
if (spriteSize > MAX_SPRITESHEET_SIZE) {
2019-10-08 14:57:10 +00:00
numberOfSheetsThatAreTooBig += 1;
const name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
}
});
if (numberOfSheetsThatAreTooBig > 0) {
// https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
console.error( // eslint-disable-line no-console
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
them, but there is a margin of error in these calculations so it is probably okay. Mention
2019-10-08 14:57:10 +00:00
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`,
);
} else {
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
}
done();
}));
gulp.task('sprites:compile', gulp.series('sprites:clean', 'sprites:checkCompiledDimensions', done => done()));