gatsby-image
最近更新できてないのでリポジトリだけ置いておく
v2からv3へのmigrateガイド見ながら
下記のdocumentを読む最終目標
v2で作った下記のリポジトリv3では+avifまで対応できるっぽいのでそれもやる
最終目標
- ✅Gatsby Imageを使った画像最適化
- ✅全画像から特定の画像を返す汎用componentを作成する
- ✅アートディレクションにも自動で対応する
- ✅SSGした時の初回ロードでアートディレクションのアスペクト比がバグっている(v2)部分を確認対応
- ✅画像がない場合はエラーを投げる
- ✅TS対応
- ❌AVIF対応(AVIFは画質が悪いため断念)
フロー
- ✅document読みながら適当に作る
- ✅とりあえず、動くまで作る
- ✅リファクタリングする
- ドキュメント書く
StaticImage強すぎない?大抵の場合こっちで良いかもだけど
ここみるとwrapperは作れなさそうあとアートディレクションに非対応の致命的な欠点がある
比較
StaticImage
import React, {FCP} from 'react';
import {Layout, SEO} from '@src/layouts';
import {StaticImage} from "gatsby-plugin-image";
const IndexPage: FCP = () => {
return (
<Layout>
<SEO/>
<h1 className="text-4xl">index.page</h1>
<StaticImage src="../images/screenshot.png" alt="" formats={["auto", "webp", "avif"]}/>
</Layout>
);
};
export default IndexPage;
GatsbyImage
import React, {FCP} from 'react';
import {Layout, SEO} from '@src/layouts';
import {GatsbyImage} from "gatsby-plugin-image";
import {graphql, useStaticQuery} from 'gatsby';
const IndexPage: FCP = () => {
const images = useStaticQuery(graphql`
query MyQuery {
file(relativePath: {eq: "screenshot.png"}) {
childImageSharp {
gatsbyImageData(layout: CONSTRAINED, formats: [AUTO, WEBP, AVIF])
}
}
}
`);
return (
<Layout>
<SEO/>
<h1 className="text-4xl">index.page</h1>
<GatsbyImage image={images.file.childImageSharp.gatsbyImageData} alt="" />
</Layout>
);
};
export default IndexPage;
当然HTMLは同じ
というかpadding-bottomでアスペクト比取ってたdiv消えたんですね
まぁwidthとheightが指定してあるから必要ないっちゃ無いけど
StaticImage楽でいいけどpath違うと急に画像でなくなる🤔
あとpathは../imagesで固定っぽい?
low/low/lowとかSSGするとpath変わりそう
それなら相対パス割り出さないといけないし手動で書くのめんどくさいなぁ
現状StaticImageまとめ
- GatsbyImageで表示するのとほぼ同じ事ができる
- 何回層落ちてもpathは
../images
で固定 - SSGするとpathはルート相対パス(
/static/
)になるのでこれも何回層落ちても関係無さそう - pathがあって無いと表示されない(エラー等出ない)
- アートディレクション(PC,SPで違う画像)には未対応
- StaticImageのwrapper componentは制約が厳しいので原則作れない(下記参考)
https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#restrictions-on-using-staticimage
ぶっちゃけアートディレクションそんなに使わ無いけど、エラー出せたりwrapper作れるならGatsbyImageでもいい感じあるな🤔
とりあえずGatsbyImageの方の汎用componentも作っていこう
仕様
- 画像ファイルをすべて予め取得しておいて、
relativePath
を受け取り表示したい画像を返す - 画像がない場合はエラーを吐く
- prefix
sp_
から始まるファイルはアートディレクションとして処理する - その他
import React, {FCX} from 'react';
import {graphql, useStaticQuery} from 'gatsby';
import {GatsbyImage, IGatsbyImageData} from 'gatsby-plugin-image';
type PicturePropsType = {
relativePath: string;
};
export const Picture: FCX<PicturePropsType> = ({relativePath}) => {
const images = useStaticQuery<{
allFile: {
nodes: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
}[];
};
}>(graphql`
query AllImages {
allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: CONSTRAINED
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentImages = images.allFile.nodes.find(
(n) => n.relativePath === relativePath
);
if (!currentImages) {
return (
<div>{relativePath}は存在しないよ</div>
)
} else {
return (
<div>
<GatsbyImage
image={currentImages.childImageSharp.gatsbyImageData}
alt=""
/>
</div>
)
}
};
-
sourceInstanceName
にはgatsby-config.js
のgatsby-transformer-sharp.options.name
値を指定してあげる -
extension
(拡張子)の絞り込みを正規表現で/(png|jpe?g)/
とする事でpng,jpg,jpeg
のみを対象として画像を取ってくる -
images.allFile.nodes
配列なのでfind
で最初に見つけた一つを切り出して返す - currentImagesが無い時はエラーを返す
今日はもう時間切れなので、アートディレクションの処理はまた明日以降
PC版とSP版の画像切り替えをするためにクエリを分ける
import React, {FCX} from 'react';
import {graphql, useStaticQuery} from 'gatsby';
import {GatsbyImage, IGatsbyImageData} from 'gatsby-plugin-image';
type PicturePropsType = {
relativePath: string;
};
type PictureQueryType = {
nodes: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
}[];
};
export const Picture: FCX<PicturePropsType> = ({relativePath}) => {
const {desktopImages, mobileImages} = useStaticQuery<{
desktopImages: PictureQueryType;
mobileImages: PictureQueryType
}>(graphql`
query AllImages {
desktopImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^(?!sp_)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: CONSTRAINED
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
},
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: CONSTRAINED
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentDesktopImage = desktopImages.nodes.find(
(n) => n.relativePath === relativePath
);
const currentMobileImage = currentDesktopImage ? mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
}) : undefined;
if (!currentDesktopImage) {
return <div>{relativePath}は存在しないよ</div>;
} else {
return (
<div>
<GatsbyImage
image={currentDesktopImage.childImageSharp.gatsbyImageData}
alt=""
/>
</div>
);
}
};
アートディレクション対応をする
とりあえずアートディレクションが動くまで
import React, {FCX} from 'react';
import {graphql, useStaticQuery} from 'gatsby';
import {GatsbyImage, IGatsbyImageData, withArtDirection} from 'gatsby-plugin-image';
type PicturePropsType = {
relativePath: string;
};
type PictureQueryType = {
nodes: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
}[];
};
export const Picture: FCX<PicturePropsType> = ({relativePath}) => {
const {desktopImages, mobileImages} = useStaticQuery<{
desktopImages: PictureQueryType;
mobileImages: PictureQueryType
}>(graphql`
query AllImages {
desktopImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^(?!sp_)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: CONSTRAINED
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
},
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: CONSTRAINED
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentDesktopImage = desktopImages.nodes.find(
(n) => n.relativePath === relativePath
);
if (!currentDesktopImage) {
return <div>{relativePath}は存在しないよ</div>;
}
const currentMobileImage = mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
});
function test({desktopImage, mobileImage}: {
desktopImage: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
}
},
mobileImage: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
}
} | undefined,
}) {
if (desktopImage && mobileImage) {
return withArtDirection(desktopImage.childImageSharp.gatsbyImageData, [
{
media: "(max-width: 1024px)",
image: mobileImage.childImageSharp.gatsbyImageData,
},
]);
} else {
return desktopImage.childImageSharp.gatsbyImageData;
}
}
const images = test({desktopImage: currentDesktopImage, mobileImage: currentMobileImage});
return (
<div>
<GatsbyImage
image={images}
alt=""
/>
</div>
);
};
アートディレクションに対応できた!
padding-bottomがなくなったのでアスペクト比が違う画像の場合はPC版のアスペクト比のまま変わらないみたい
ここも手動で直す
graphql見る限りgatsbyImageDataにwidthとheightが含まれるっぽいのでそこからアスペクト比を割り出した、divなどを作りcssでdisplay切り替えすれば良さそう
無論JSでも行けそうだけSSG考えるとバグるかも?
ちなみにv2ではwidthとheightは取れなかった記憶がある
presentationWidth
とpresentationHeight
を別に取ってこないといけなかったのがwidthとheightで取れるのは楽でいいね😸
import React, {FCX} from "react";
type AspectRatioPropsType = {
width: number
height: number
}
export const AspectRatio: FCX<AspectRatioPropsType> = (
{
className="",
children,
width,
height
}) => {
return (
<div className={className} style={{paddingTop: `${height/width*100}%`}}>
{children}
</div>
)
}
import React, {FCX} from 'react';
import {graphql, useStaticQuery} from 'gatsby';
import {GatsbyImage, IGatsbyImageData, withArtDirection} from 'gatsby-plugin-image';
import {AspectRatio} from "@src/components";
type PicturePropsType = {
relativePath: string;
};
type PictureQueryType = {
nodes: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
}[];
};
export const Picture: FCX<PicturePropsType> = ({relativePath}) => {
const {desktopImages, mobileImages} = useStaticQuery<{
desktopImages: PictureQueryType;
mobileImages: PictureQueryType
}>(graphql`
query AllImages {
desktopImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^(?!sp_)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
},
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentDesktopImage = desktopImages.nodes.find(
(n) => n.relativePath === relativePath
);
if (!currentDesktopImage) {
return <div>{relativePath}は存在しないよ</div>;
}
const currentMobileImage = mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
});
function test({desktopImage, mobileImage}: {
desktopImage: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
}
},
mobileImage: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
}
} | undefined,
}) {
if (desktopImage && mobileImage) {
return withArtDirection(mobileImage.childImageSharp.gatsbyImageData, [
{
media: "(min-width: 768px)",
image: desktopImage.childImageSharp.gatsbyImageData,
},
]);
} else {
return desktopImage.childImageSharp.gatsbyImageData;
}
}
const images = test({desktopImage: currentDesktopImage, mobileImage: currentMobileImage});
return (
<div className="relative">
{currentMobileImage && <AspectRatio className="block md:hidden" width={currentMobileImage.childImageSharp.gatsbyImageData.width} height={currentMobileImage.childImageSharp.gatsbyImageData.height}/>}
<AspectRatio className="hidden md:block" width={currentDesktopImage.childImageSharp.gatsbyImageData.width} height={currentDesktopImage.childImageSharp.gatsbyImageData.height}/>
<GatsbyImage
className="absolute inset-0"
image={images}
alt=""
/>
</div>
);
};
いや、padding-bottom出てくるんかい!
ということでこの邪魔なpadding-bottomを消す
ちなみにv2ではSSGした時にこのpadding-bottomのレンダリングがうまくいかないみたいで、画像はちゃんと表示されるけど、アスペクト比はあってないみたいなバグ(?)があった
SSGするとtailwindがinlineになるから.gatsby-image-wrapperに負けてrelativeになるのね
まぁtailwindは!important安定かな
import React, {FCX} from 'react';
import {graphql, useStaticQuery} from 'gatsby';
import {GatsbyImage, IGatsbyImageData, withArtDirection} from 'gatsby-plugin-image';
import {AspectRatio} from "@src/components";
import * as styles from "@src/components/Picture/picture.module.css";
type PicturePropsType = {
relativePath: string;
};
type PictureQueryType = {
nodes: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
}[];
};
export const Picture: FCX<PicturePropsType> = ({relativePath}) => {
const {desktopImages, mobileImages} = useStaticQuery<{
desktopImages: PictureQueryType;
mobileImages: PictureQueryType
}>(graphql`
query AllImages {
desktopImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^(?!sp_)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
},
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentDesktopImage = desktopImages.nodes.find(
(n) => n.relativePath === relativePath
);
if (!currentDesktopImage) {
return <div>{relativePath}は存在しないよ</div>;
}
const currentMobileImage = mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
});
function test({desktopImage, mobileImage}: {
desktopImage: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
}
},
mobileImage: {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
}
} | undefined,
}) {
if (desktopImage && mobileImage) {
return withArtDirection(mobileImage.childImageSharp.gatsbyImageData, [
{
media: "(min-width: 768px)",
image: desktopImage.childImageSharp.gatsbyImageData,
},
]);
} else {
return desktopImage.childImageSharp.gatsbyImageData;
}
}
const images = test({desktopImage: currentDesktopImage, mobileImage: currentMobileImage});
return (
<div className="relative">
{currentMobileImage && <AspectRatio className="block md:hidden" width={currentMobileImage.childImageSharp.gatsbyImageData.width} height={currentMobileImage.childImageSharp.gatsbyImageData.height}/>}
<AspectRatio className="hidden md:block" width={currentDesktopImage.childImageSharp.gatsbyImageData.width} height={currentDesktopImage.childImageSharp.gatsbyImageData.height}/>
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt=""
/>
</div>
);
};
.gatsby-image-wrapper:not(#id) {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.gatsby-image-wrapper:not(#id) [aria-hidden="true"][style*="padding-top"] {
display: none;
}
SSGした時に.gatsby-image-wrapper
に詳細度で負けるので:not(#id)
で無理矢理上げて勝つ
現状比較
StaticImage
- アートディレクションが不要
- srcの部分は手動(pathが違うと表示されない)
- ぶっちゃけ上記の2点を除けば、GatsbyImageと同等
- 基本的にwrapperが作れない
GatsbyImage
- アートディレクションが必要
- srcの部分は手動(pathが違うとエラーを投げることが可能)
- graphql書くのがだるいけど、エラー投げれたりする
- wrapperが作れる
まとめ
StaticImageはお手軽に使えるけど、graphqlを書いていればGatsbyImageで良い感ある
雰囲気でtypescriptやってるからできるかわからないけど、graphqlからrelativePathの型を作れそう
というかぶっちゃけアスペクト比取ってるpadding-topを消して今回みたいに自分でアスペクト比取れば、layoutはFULL_WIDTH
で良いんじゃない?と思う。
例えば、アスペクト比固定したい時は手動でアスペクト比のやつを入れるようにすればいいし、高さ固定の場合はアスペクト比を入れなければいいし、横幅はmax-widthで制御するはずなので、もとの画像より大きい方が都合が良さそう
というかStaticImageのsrcはどうかけば良いのか
Source image, processed at build time. Can be a path relative to the source file, or an absolute URL.
なぜかpathはミスってても表示されるっぽい
warn Could not find image "../../images/screenshot.png". Looked for
相対パスミスって上記のwarnが出ても表示される
ちなみにルート相対パス/src/images
は表示されない
いけるpath
/low/
→../../
、../../../
、../../../../
/low/low/
→../../
、../../../
、../../../../
/low/low/low/
→../../
、../../../
、../../../../
ほんと謎😂
とりあえずgatsby develop
した時に見えたらSSGしても見えるっぽい
とりあえず、外部切り出し
import { graphql, useStaticQuery } from 'gatsby';
import { IGatsbyImageData } from 'gatsby-plugin-image';
export type anyImageQueryType = {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
};
export type PictureQueryType = {
nodes: anyImageQueryType[];
};
type useAnyImageType = (
relativePath: string
) => {
anyImage?: anyImageQueryType;
mobileImage?: anyImageQueryType;
};
export const useAnyImage: useAnyImageType = (relativePath) => {
const { anyImages, mobileImages } = useStaticQuery<{
anyImages: PictureQueryType;
mobileImages: PictureQueryType;
}>(graphql`
query AllImageas {
anyImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentAnyImage = anyImages.nodes.find(
(n) => n.relativePath === relativePath
);
const currentMobileImage = mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
});
return {
anyImage: currentAnyImage,
mobileImage: currentMobileImage,
};
};
import React, { FCX } from 'react';
import { GatsbyImage, withArtDirection } from 'gatsby-plugin-image';
import { AspectRatio } from '@src/components';
import * as styles from '@src/components/Picture/picture.module.css';
import { useAnyImage, anyImageQueryType } from '@src/hooks';
type PicturePropsType = {
relativePath: string;
};
export const Picture: FCX<PicturePropsType> = ({ relativePath }) => {
const { anyImage, mobileImage } = useAnyImage(relativePath);
if (!anyImage) {
return <div>{`${relativePath}は存在しません!`}</div>;
}
function test({
desktopImage,
mobileImage,
}: {
desktopImage: anyImageQueryType;
mobileImage?: anyImageQueryType;
}) {
if (desktopImage && mobileImage) {
return withArtDirection(mobileImage.childImageSharp.gatsbyImageData, [
{
media: '(min-width: 768px)',
image: desktopImage.childImageSharp.gatsbyImageData,
},
]);
} else {
return desktopImage.childImageSharp.gatsbyImageData;
}
}
const images = test({
desktopImage: anyImage,
mobileImage,
});
return (
<div className="relative">
{mobileImage && (
<AspectRatio
className="block md:hidden"
width={mobileImage.childImageSharp.gatsbyImageData.width}
height={mobileImage.childImageSharp.gatsbyImageData.height}
/>
)}
<AspectRatio
className="hidden md:block"
width={anyImage.childImageSharp.gatsbyImageData.width}
height={anyImage.childImageSharp.gatsbyImageData.height}
/>
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt=""
/>
</div>
);
};
リファクタリングその2
アートディレクションの処理をuseAnyImageに移動
import { graphql, useStaticQuery } from 'gatsby';
import { IGatsbyImageData, withArtDirection } from 'gatsby-plugin-image';
export type anyImageQueryType = {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
};
export type PictureQueryType = {
nodes: anyImageQueryType[];
};
type createArtDirectionType = (
images: {
desktopImage: anyImageQueryType;
mobileImage?: anyImageQueryType;
},
media?: string
) => IGatsbyImageData;
type useAnyImageType = (
relativePath: string
) => {
anyImage?: anyImageQueryType;
mobileImage?: anyImageQueryType;
createArtDirection: createArtDirectionType;
};
export const useAnyImage: useAnyImageType = (relativePath) => {
const { anyImages, mobileImages } = useStaticQuery<{
anyImages: PictureQueryType;
mobileImages: PictureQueryType;
}>(graphql`
query AllImageas {
anyImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentAnyImage = anyImages.nodes.find(
(n) => n.relativePath === relativePath
);
const currentMobileImage = mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
});
const createArtDirection: createArtDirectionType = (
{ desktopImage, mobileImage },
media = `min-width: ${768 / 16}em`
) => {
if (desktopImage && mobileImage) {
return withArtDirection(mobileImage.childImageSharp.gatsbyImageData, [
{
media: `(${media})`,
image: desktopImage.childImageSharp.gatsbyImageData,
},
]);
} else {
return desktopImage.childImageSharp.gatsbyImageData;
}
};
return {
anyImage: currentAnyImage,
mobileImage: currentMobileImage,
createArtDirection,
};
};
import React, { FCX } from 'react';
import { GatsbyImage } from 'gatsby-plugin-image';
import { AspectRatio } from '@src/components';
import * as styles from '@src/components/Picture/picture.module.css';
import { useAnyImage } from '@src/hooks';
type PicturePropsType = {
relativePath: string;
};
export const Picture: FCX<PicturePropsType> = ({ relativePath }) => {
const { anyImage, mobileImage, createArtDirection } = useAnyImage(
relativePath
);
if (!anyImage) {
return <div>{`${relativePath}は存在しません!`}</div>;
}
const images = createArtDirection({ desktopImage: anyImage, mobileImage });
return (
<div className="relative">
{mobileImage && (
<AspectRatio
className="block md:hidden"
width={mobileImage.childImageSharp.gatsbyImageData.width}
height={mobileImage.childImageSharp.gatsbyImageData.height}
/>
)}
<AspectRatio
className="hidden md:block"
width={anyImage.childImageSharp.gatsbyImageData.width}
height={anyImage.childImageSharp.gatsbyImageData.height}
/>
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt=""
/>
</div>
);
};
機能追加その1
アスペクト比の有無を自由に決めれるように
import React, { FCX } from 'react';
import { GatsbyImage } from 'gatsby-plugin-image';
import { AspectRatio } from '@src/components';
import * as styles from '@src/components/Picture/picture.module.css';
import { useAnyImage } from '@src/hooks';
type PicturePropsType = {
aspect?: boolean;
relativePath: string;
};
export const Picture: FCX<PicturePropsType> = ({
aspect = true,
relativePath,
}) => {
const { anyImage, mobileImage, createArtDirection } = useAnyImage(
relativePath
);
if (!anyImage) {
return <div>{`${relativePath}は存在しません!`}</div>;
}
const images = createArtDirection({ desktopImage: anyImage, mobileImage });
return (
<div className="relative w-full h-full">
{aspect && mobileImage && (
<AspectRatio
className="block sm:hidden"
width={mobileImage.childImageSharp.gatsbyImageData.width}
height={mobileImage.childImageSharp.gatsbyImageData.height}
/>
)}
{aspect && (
<AspectRatio
className="hidden sm:block"
width={anyImage.childImageSharp.gatsbyImageData.width}
height={anyImage.childImageSharp.gatsbyImageData.height}
/>
)}
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt=""
/>
</div>
);
};
機能追加その2
Piccture
にGatsbyImageProps
を渡せるようにする
import React, { FCX } from 'react';
import { GatsbyImage, GatsbyImageProps } from 'gatsby-plugin-image';
import { AspectRatio } from '@src/components';
import * as styles from '@src/components/Picture/picture.module.css';
import { useAnyImage } from '@src/hooks';
type PicturePropsType = {
aspect?: boolean;
relativePath: string;
alt?: string;
GatsbyImageProps?: Omit<GatsbyImageProps, 'alt' | 'image' | 'className'>;
};
export const Picture: FCX<PicturePropsType> = ({
aspect = true,
relativePath,
alt = '',
GatsbyImageProps = {},
}) => {
const { anyImage, mobileImage, createArtDirection } = useAnyImage(
relativePath
);
if (!anyImage) {
return <div>{`${relativePath}は存在しません!`}</div>;
}
const images = createArtDirection({ desktopImage: anyImage, mobileImage });
return (
<div className="relative w-full h-full">
{aspect && mobileImage && (
<AspectRatio
className="block sm:hidden"
width={mobileImage.childImageSharp.gatsbyImageData.width}
height={mobileImage.childImageSharp.gatsbyImageData.height}
/>
)}
{aspect && (
<AspectRatio
className="hidden sm:block"
width={anyImage.childImageSharp.gatsbyImageData.width}
height={anyImage.childImageSharp.gatsbyImageData.height}
/>
)}
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt={alt}
{...GatsbyImageProps}
/>
</div>
);
};
機能追加 その3
sp版の画像がない場合はpc,spで同じアスペクト比を維持する
import React, { FCX } from 'react';
import { GatsbyImage, GatsbyImageProps } from 'gatsby-plugin-image';
import { AspectRatio } from '@src/components';
import * as styles from '@src/components/Picture/picture.module.css';
import { useAnyImage } from '@src/hooks';
type PicturePropsType = {
aspect?: boolean;
relativePath: string;
alt?: string;
GatsbyImageProps?: Omit<GatsbyImageProps, 'alt' | 'image' | 'className'>;
};
export const Picture: FCX<PicturePropsType> = ({
aspect = true,
relativePath,
alt = '',
GatsbyImageProps = {},
}) => {
const { anyImage, mobileImage, createArtDirection } = useAnyImage(
relativePath
);
if (!anyImage) {
return <div>{`${relativePath}は存在しません!`}</div>;
}
const images = createArtDirection({ desktopImage: anyImage, mobileImage });
return (
<div className="relative w-full h-full">
{aspect && mobileImage && (
<AspectRatio
className="block sm:hidden"
width={mobileImage.childImageSharp.gatsbyImageData.width}
height={mobileImage.childImageSharp.gatsbyImageData.height}
/>
)}
{aspect && (
<AspectRatio
className={mobileImage && `hidden sm:block`}
width={anyImage.childImageSharp.gatsbyImageData.width}
height={anyImage.childImageSharp.gatsbyImageData.height}
/>
)}
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt={alt}
{...GatsbyImageProps}
/>
</div>
);
};
とりあえず現状まとめ
import { graphql, useStaticQuery } from 'gatsby';
import { IGatsbyImageData, withArtDirection } from 'gatsby-plugin-image';
export type anyImageQueryType = {
relativePath: string;
childImageSharp: {
gatsbyImageData: IGatsbyImageData;
};
};
export type PictureQueryType = {
nodes: anyImageQueryType[];
};
type createArtDirectionType = (
images: {
desktopImage: anyImageQueryType;
mobileImage?: anyImageQueryType;
},
media?: string
) => IGatsbyImageData;
type useAnyImageType = (
relativePath: string
) => {
anyImage?: anyImageQueryType;
mobileImage?: anyImageQueryType;
createArtDirection: createArtDirectionType;
};
export const useAnyImage: useAnyImageType = (relativePath) => {
const { anyImages, mobileImages } = useStaticQuery<{
anyImages: PictureQueryType;
mobileImages: PictureQueryType;
}>(graphql`
query AllImageas {
anyImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
mobileImages: allFile(
filter: {
sourceInstanceName: { eq: "images" }
extension: { regex: "/(png|jpe?g)/" }
name: { regex: "/^sp_/" }
}
) {
nodes {
relativePath
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
formats: [AUTO, WEBP, AVIF]
placeholder: BLURRED
)
}
}
}
}
`);
const currentAnyImage = anyImages.nodes.find(
(n) => n.relativePath === relativePath
);
const currentMobileImage = mobileImages.nodes.find((n) => {
return n.relativePath.replace(/sp_/, '') === relativePath;
});
const createArtDirection: createArtDirectionType = (
{ desktopImage, mobileImage },
media = `min-width: ${768 / 16}em`
) => {
if (desktopImage && mobileImage) {
return withArtDirection(mobileImage.childImageSharp.gatsbyImageData, [
{
media: `(${media})`,
image: desktopImage.childImageSharp.gatsbyImageData,
},
]);
} else {
return desktopImage.childImageSharp.gatsbyImageData;
}
};
return {
anyImage: currentAnyImage,
mobileImage: currentMobileImage,
createArtDirection,
};
};
.gatsby-image-wrapper:not(#id) {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.gatsby-image-wrapper:not(#id) [aria-hidden="true"][style*="padding-top"] {
display: none;
}
import React, { FCX } from 'react';
import { GatsbyImage, GatsbyImageProps } from 'gatsby-plugin-image';
import { AspectRatio } from '@src/components';
import * as styles from '@src/components/Picture/picture.module.css';
import { useAnyImage } from '@src/hooks';
type PicturePropsType = {
aspect?: boolean;
relativePath: string;
alt?: string;
GatsbyImageProps?: Omit<GatsbyImageProps, 'alt' | 'image' | 'className'>;
};
export const Picture: FCX<PicturePropsType> = ({
aspect = true,
relativePath,
alt = '',
GatsbyImageProps = {},
}) => {
const { anyImage, mobileImage, createArtDirection } = useAnyImage(
relativePath
);
if (!anyImage) {
return <div>{`${relativePath}は存在しません!`}</div>;
}
const images = createArtDirection({ desktopImage: anyImage, mobileImage });
return (
<div className="relative w-full h-full">
{aspect && mobileImage && (
<AspectRatio
className="block sm:hidden"
width={mobileImage.childImageSharp.gatsbyImageData.width}
height={mobileImage.childImageSharp.gatsbyImageData.height}
/>
)}
{aspect && (
<AspectRatio
className={mobileImage && `hidden sm:block`}
width={anyImage.childImageSharp.gatsbyImageData.width}
height={anyImage.childImageSharp.gatsbyImageData.height}
/>
)}
<GatsbyImage
className={styles.gatsbyImageWrapper}
image={images}
alt={alt}
{...GatsbyImageProps}
/>
</div>
);
};
呼び出し
import React, { FCP } from 'react';
import { Layout, SEO } from '@src/layouts';
import { Picture } from '@src/components';
const IndexPage: FCP = () => {
return (
<Layout>
<SEO />
<Picture relativePath="screenshot.png" /> {/* ps,pcでアスペクト比が違う画像 */}
<Picture relativePath="icon.png" />{/* ps,pcで何も変わらない画像 */}
<div className="w-20 h-40">
<Picture aspect={false} relativePath="screenshot.png" />{/* アスペクト比を無視したい画像 */}
</div>
</Layout>
);
};
export default IndexPage;
コメントアウト書かないの良くないなぁ😂