自前対応する場合の備忘
出し分けたい条件
- jpg,png,svgに対応する
- jpgとpngの場合はwebp対応する
- 画面幅によってスマホ用とパソコン用の画像を出しわける
- スマホ用の画像はファイル名には末尾に「_sp」をつける
- スマホ用の画像を指定しない場合も対応する
- Retina対応として、パソコンの場合は1倍と2倍、スマホは2倍のみとする
- 2倍書き出しの画像はファイル名には末尾に「_@2x」をつける
🐖pug
mixin Picture(fileName, format, alt, width, height, media=true)
- const path = "./assets/images/"
if format !== "svg"
picture
if media
source(media="(max-width: 1024px)" srcset=`${path}${fileName}_sp.webp` type="image/webp")
source(media="(max-width: 1024px)" srcset=`${path}${fileName}_sp.${format}` type="image/jpg")
source(media="(min-width: 1025px)" srcset=`${path}${fileName}.webp ${path}${fileName}@2x.webp 2x` type="image/webp")
img(src=`${path}${fileName}.${format}` alt=`${alt}` width=`${width}` height=`${height}`)
else
source(srcset=`${path}${fileName}.webp ${path}${fileName}@2x.webp 2x` type="image/webp")
img(src=`${path}${fileName}.${format}` alt=`${alt}` width=`${width}` height=`${height}`)
else
picture
img(src=`${path}${fileName}.${format}` alt=`${alt}` width=`${width}` height=`${height}`)
Vue(v3)
<template>
<ClientOnly>
<picture class="client_only_picture" :class="className">
<!-- retina出しわけ SVGなら2倍は設定しない -->
<template v-if="retinaImg">
<source
:srcset="`${requireImg(file)} 1x, ${requireImg(retinaImg)} 2x`"
:media="spFile ? 'min-width:769px' : ''"
/>
</template>
<template v-else-if="file">
<source
:srcset="requireImg(file)"
:media="spFile ? 'min-width:769px' : ''"
/>
</template>
<!-- SP出しわけ -->
<template v-if="spFile">
<source :srcset="requireImg(spFile)" media="(max-width: 768px)" />
</template>
<img
:src="file ? requireImg(file) : requireImg(spFile)"
:alt="alt"
:width="width"
:height="height"
:loading="loading"
decoding="async"
/>
</picture>
</ClientOnly>
</template>
<script lang="ts" setup>
type Props = {
file: string
spFile?: string
alt: string
width?: string
height?: string
isRetina?: boolean
loading?: "lazy" | "eager" | undefined
className?: string
}
const props = withDefaults(defineProps<Props>(), {
file: "",
spFile: "",
alt: "",
width: "",
height: "",
isRetina: false,
loading: undefined,
className: "",
})
const retinaImg = computed(() => {
const reg = /\.(png|jpg|webp|gif)/i
return props.isRetina && !props.file.includes(".svg")
? props.file.replace(reg, "@2x.$1")
: null
})
const requireImg = (file: string) => {
const img = new URL(`../../assets/images/${file}`, import.meta.url).href
return img
}
</script>
Vue(v2)
TODO
React
もっといい方法ありそう…
'use client';
/**
* @des 画像のファイル名を取得
* @param {String} name
* @returns String
*/
const getImageFileName = (name) => {
const lastDotIndex = name.lastIndexOf('.');
if (lastDotIndex !== -1) {
return name.substring(0, lastDotIndex);
}
return name;
};
const DefaultPicture = ({
src = '',
file = '',
spFile = '',
extension = '',
alt = '',
width = null,
height = null,
webpAlt = 'jpg',
loading = '',
isRetina = true,
className = '',
}) => {
const fileName = file ? getImageFileName(file) : '';
const retinaImg = isRetina ? `${file}@2x` : null;
return (
<picture className={className}>
{/* SP用:幅768px以下なら表示 */}
{spFile && (
<>
<source
srcSet={`/assets/images${src}/${spFile}.${extension}`}
type={`image/${extension}`}
media="(max-width: 768px)"
/>
{/* WEBP:代替 */}
{extension === 'webp' && (
<source
srcSet={`/assets/images${src}/${spFile}.${webpAlt}`}
type={`image/${webpAlt}`}
media="(max-width: 768px)"
/>
)}
</>
)}
{extension === 'svg' ? (
<source
srcSet={`/assets/images${src}/${file}.${extension}`}
type="image/svg"
/>
) : retinaImg ? (
<>
<source
srcSet={`/assets/images${src}/${file}.${extension} 1x, /assets/images${src}/${retinaImg}.${extension} 2x`}
type={`image/${extension}`}
media={spFile ? '(min-width:769px)' : null}
/>
{/* WEBP:代替 */}
{extension === 'webp' && (
<source
srcSet={`/assets/images${src}/${fileName}.${webpAlt} 1x, /assets/images${src}/${retinaImg}.${webpAlt} 2x`}
type={`image/${webpAlt}`}
media={spFile ? '(min-width:769px)' : null}
/>
)}
</>
) : (
<>
<source
srcSet={`/assets/images${src}/${file}.${extension}`}
type={`image/${extension}`}
media={spFile ? '(min-width:769px)' : null}
/>
{/* WEBP:代替 */}
{extension === 'webp' && (
<source
srcSet={`/assets/images${src}/${fileName}.${webpAlt}`}
type={`image/${webpAlt}`}
media={spFile ? '(min-width:769px)' : null}
/>
)}
</>
)}
{/* デフォルト */}
<img
src={`/assets/images${src}/${file}.${extension}`}
alt={alt}
width={width}
height={height}
loading={loading}
/>
</picture>
);
};
const PictureImage = (props) => {
return <DefaultPicture {...props} />;
};
export default PictureImage;