diff --git a/lib/resize.cjs b/lib/resize.cjs index c11afc7..9005db8 100644 --- a/lib/resize.cjs +++ b/lib/resize.cjs @@ -2,14 +2,32 @@ const { remove, mkdirp } = require('fs-extra') const { join } = require('pathe') const sharp = require('sharp') -async function resize ({ input, distDir, sizes, suffix }) { +async function resize ({ input, distDir, sizes, suffix, backgroundColor, devices, mobileAppIOS }) { await remove(distDir) await mkdirp(distDir) + await Promise.all(sizes.map(size => sharp(input) .resize(size, size) .toFile(join(distDir, `${size}x${size}${suffix}.png`)) )) + + if (mobileAppIOS) { + await Promise.all(devices.map(device => + sharp({ + create: { + width: device.width, + height: device.height, + channels: 4, + background: backgroundColor + } + }).composite([ + { input } + ]) + .png() + .toFile(join(distDir, `${device.width}x${device.height}-splash-screen.png`)) + )) + } } resize(JSON.parse(process.argv[2])).then(() => { diff --git a/src/devices.ts b/src/devices.ts new file mode 100644 index 0000000..71a3cf8 --- /dev/null +++ b/src/devices.ts @@ -0,0 +1,29 @@ +export default [ + { width: 2732, height: 2048, pixelRatio: 2, orientation: 'landscape' }, + { width: 1668, height: 2388, pixelRatio: 2, orientation: 'portrait' }, + { width: 2388, height: 1668, pixelRatio: 2, orientation: 'landscape' }, + { width: 1536, height: 2048, pixelRatio: 2, orientation: 'portrait' }, + { width: 2048, height: 1536, pixelRatio: 2, orientation: 'landscape' }, + { width: 1668, height: 2224, pixelRatio: 2, orientation: 'portrait' }, + { width: 2224, height: 1668, pixelRatio: 2, orientation: 'landscape' }, + { width: 1620, height: 2160, pixelRatio: 2, orientation: 'portrait' }, + { width: 2160, height: 1620, pixelRatio: 2, orientation: 'landscape' }, + { width: 1284, height: 2778, pixelRatio: 2, orientation: 'portrait' }, + { width: 2778, height: 1284, pixelRatio: 3, orientation: 'landscape' }, + { width: 1170, height: 2532, pixelRatio: 3, orientation: 'portrait' }, + { width: 2532, height: 1170, pixelRatio: 3, orientation: 'landscape' }, + { width: 1125, height: 2436, pixelRatio: 3, orientation: 'portrait' }, + { width: 2436, height: 1125, pixelRatio: 3, orientation: 'landscape' }, + { width: 1242, height: 2688, pixelRatio: 3, orientation: 'portrait' }, + { width: 2688, height: 1242, pixelRatio: 3, orientation: 'landscape' }, + { width: 828, height: 1792, pixelRatio: 2, orientation: 'portrait' }, + { width: 1792, height: 828, pixelRatio: 2, orientation: 'landscape' }, + { width: 1242, height: 2208, pixelRatio: 3, orientation: 'portrait' }, + { width: 2208, height: 1242, pixelRatio: 3, orientation: 'landscape' }, + { width: 750, height: 1334, pixelRatio: 2, orientation: 'portrait' }, + { width: 1334, height: 750, pixelRatio: 2, orientation: 'landscape' }, + { width: 640, height: 1136, pixelRatio: 2, orientation: 'portrait' }, + { width: 1136, height: 640, pixelRatio: 2, orientation: 'landscape' } +] + +// https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/#device-screen-sizes-and-orientations diff --git a/src/icon.ts b/src/icon.ts index 2bf6aef..b10ec6e 100644 --- a/src/icon.ts +++ b/src/icon.ts @@ -5,6 +5,7 @@ import hasha from 'hasha' import { join, resolve } from 'pathe' import { useNuxt } from '@nuxt/kit' import type { PWAContext } from './types' +import devices from './devices' async function getFileHash (filePath: string): Promise { const hash = await hasha.fromFile(filePath, { algorithm: 'md5' }) @@ -47,11 +48,18 @@ export default async (pwa: PWAContext) => { }) } + if (options.splash.devices.length === 0) { + options.splash.devices = devices + } + const resizeOptions = JSON.stringify({ input: options.source, distDir: join(pwa._assetsDir, options.targetDir), sizes: options.sizes, - suffix: iconSuffix + suffix: iconSuffix, + backgroundColor: pwa.manifest.background_color, + mobileAppIOS: options.mobileAppIOS, + devices: options.splash.devices }) let generate: Promise @@ -65,7 +73,7 @@ export default async (pwa: PWAContext) => { const child = fork(pwa._resolver.resolve('../lib/resize.cjs'), [resizeOptions]) child.on('exit', (code: number) => code ? reject(code) : resolve()) }).then(() => { - consola.success(`PWA icons generated in ${Date.now() - start} ms`) + consola.success(`PWA icons and splash-screen generated in ${Date.now() - start} ms`) }) }) diff --git a/src/meta.ts b/src/meta.ts index f83810f..37ca841 100644 --- a/src/meta.ts +++ b/src/meta.ts @@ -1,5 +1,7 @@ import { useNuxt } from '@nuxt/kit' +import { join } from 'pathe' import type { PWAContext } from './types' +import devices from './devices' export default (pwa: PWAContext) => { if (!pwa.meta || !pwa.manifest) { return } @@ -16,6 +18,11 @@ export default (pwa: PWAContext) => { // mobileApp (IOS) if (options.mobileAppIOS) { head.meta.push({ name: 'apple-mobile-web-app-capable', content: 'yes' }) + + // inject splash-screen based on devices list + head.link.push(...devices.map(device => ( + { href: join(nuxt.options.app.buildAssetsDir, pwa.icon.targetDir, `${device.width}x${device.height}-splash-screen.png`), media: `(device-width: ${device.width / device.pixelRatio}px) and (device-height: ${device.height / device.pixelRatio}px) and (-webkit-device-pixel-ratio: ${device.pixelRatio}) and (orientation: ${device.orientation})`, rel: 'apple-touch-startup-image' } + ))) } // statusBarStyle (IOS) @@ -36,8 +43,6 @@ export default (pwa: PWAContext) => { head.link.push({ rel: 'shortcut icon', href: iconSmall.src }) head.link.push({ rel: 'apple-touch-icon', href: iconBig.src, sizes: iconBig.sizes }) } - - // TODO: Launch Screen Image (IOS) } // Title diff --git a/src/module.ts b/src/module.ts index c294592..f3caedb 100644 --- a/src/module.ts +++ b/src/module.ts @@ -15,7 +15,13 @@ export default defineNuxtModule({ source: null, sizes: [], fileName: 'icon.png', - targetDir: 'icons' + targetDir: 'icons', + splash: { + devices: [] + // backgroundColor, + // dedicate icon + // .. + } }, manifest: { name: process.env.npm_package_name!, diff --git a/src/types.ts b/src/types.ts index c8bbe98..27a10ec 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,7 +2,14 @@ export interface IconOptions { source: string | null fileName: string sizes: number[] - targetDir: string + targetDir: string, + splash:{ + devices: Array<{ + width: number, + height: number, + pixelRatio: number, + orientation: string + }>} } export interface MetaOptions {