LoginSignup
0
1

More than 1 year has passed since last update.

Vue コンポーネントのスタイルを MediaQuery のブレイクポイントごとに切り替える指定を親コンポーネントから渡す

Last updated at Posted at 2022-08-11

タイトルだけでは何を言ってるかわからないと思うんですが、親要素から

Parent.vue
<template>
  <Child
    :transforms="{
      pc: 'large',
      tb: 'medium',
      sp: 'small',
    }"
  />
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import Child from '~/Child.vue'

export default defineComponent({
  components: {
    Child,
  },
})
</script>

のような感じでブレイクポイントごとに特定のキーを渡してあげることで、「このページの要素A の中にある Child は PC が large、SP が small。要素B の中にある Child は PC が medium、SP が small」みたいなことが指定できるようになります。

スクリプトで CSS のメディアクエリみたいな処理を書くのでもいいんですが、CSS と二重管理になるので極力避けたいですよね。

Child.vue
<template>
  <div
    class="example"
    :data-transform-pc="dataTransformPc"
    :data-transform-tb="dataTransformTb"
    :data-transform-sp="dataTransformSp"
  >
    <p>ほげ</p>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'

const TRANSFORM_CODE_S = 'small'
const TRANSFORM_CODE_M = 'medium'
const TRANSFORM_CODE_L = 'large'

type TransformCodes =
  | typeof TRANSFORM_CODE_S
  | typeof TRANSFORM_CODE_M
  | typeof TRANSFORM_CODE_L

type Transforms = {
  pc: TransformCodes
  tb: TransformCodes
  sp: TransformCodes
}

export default defineComponent({
  props: {
    transforms: {
      type: Object as PropType<Transforms>,
      required: true,
    },
  },
  setup(props) {
    const dataTransformPc = computed(() => props.transforms.pc)
    const dataTransformTb = computed(() => props.transforms.tb)
    const dataTransformSp = computed(() => props.transforms.sp)

    return {
      dataTransformPc,
      dataTransformTb,
      dataTransformSp,
    }
  },
})
</script>

<style lang="scss" scoped>
$size-width-tb: 480px;
$size-width-pc: 768px;

@mixin is-sp-screen() {
  @media screen and (max-width: #{$size-width-tb - 1}) {
    @content;
  }
}

@mixin is-tb-screen() {
  @media screen and (min-width: $size-width-tb) and (max-width: #{$size-width-pc - 1}) {
    @content;
  }
}

@mixin is-pc-screen() {
  @media screen and (min-width: $size-width-pc) {
    @content;
  }
}

@mixin small {
  width: 240px;
  height: 240px;
  background: #eee;
}

@mixin medium {
  width: 360px;
  height: 360px;
  background: #ccc;
}

@mixin large {
  width: 480px;
  height: 480px;
  background: #aaa;
}

.example {
  &[data-transform-pc='small'] {
    @include is-pc-screen() {
      @include small;
    }
  }
  &[data-transform-tb='small'] {
    @include is-tb-screen() {
      @include small;
    }
  }
  &[data-transform-sp='small'] {
    @include is-sp-screen() {
      @include small;
    }
  }

  &[data-transform-pc='medium'] {
    @include is-pc-screen() {
      @include medium;
    }
  }
  &[data-transform-tb='medium'] {
    @include is-tb-screen() {
      @include medium;
    }
  }
  &[data-transform-sp='medium'] {
    @include is-sp-screen() {
      @include medium;
    }
  }

  &[data-transform-pc='large'] {
    @include is-pc-screen() {
      @include large;
    }
  }
  &[data-transform-tb='large'] {
    @include is-tb-screen() {
      @include large;
    }
  }
  &[data-transform-sp='large'] {
    @include is-sp-screen() {
      @include large;
    }
  }
}
</style>

ブラウザで画面幅を変えると、スタイルが一緒に変わります。
ただ、この方法だと子コンポーネント自体を切り替える みたいなことはできません。

※ 実際は tb, sp の指定は任意で、指定がなければ pc を受け継ぐみたいな処理を書いてますが、割愛してます。
※ Vue.js 以外にも、React とかでもアイデアは流用できると思います。


@mixin is-xx-screen() {} については https://qiita.com/les-r-pan/items/202cc75bd4da5170fe10

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