1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘】Pictureタグのだし分け

Last updated at Posted at 2025-01-17

自前対応する場合の備忘

出し分けたい条件

  • 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;
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?