🧮
linear-gradient + styled-componentsのドットを描画を利用してQRCodeを表示する
記事作成日: 2019-09-18
Linear Gradientだけでドット絵が作れることがわかったので作った
解説
基本
CSSのGradientには、同色を途中で切り替えると二色を切り分けるという可愛い特徴がある
export const Dotted1 = styled.div`
background: linear-gradient(red, red 0 10%, blue 10% 40%, green 40% 100%);
height: 2em;
`
例えばこれを90degにすれば
export const Dotted = styled.div`
background: linear-gradient(90deg, red, red 0 10%, blue 10% 40%, green 40% 100%);
height: 2em;
`
横並びになる
白黒を横に並べる
で、これを白黒で並べればpixelになるので、これをstyled-componentsで行うとこんな感じになる
const toColor = (v) => (v ? "#000" : "#fff")
// #000 10%, #fff 10% 20% みたいな列を作る
const generateGradient = (data) => {
const division = 100 / data.length
return data
.reduce((acc, d, i) => {
const before = i !== 0 ? `${toColor(data[i - 1])} ${division * i}` : null
const current = `${toColor(d)} ${division * i}`
return [...acc, before, current]
}, [])
.filter((i) => !!i)
.join(",")
}
export const Stripe = styled.div.attrs(({ data }) => {
const linearGradient = generateGradient(data)
return {
width: `${data.length / 2}em`,
style: { background: `linear-gradient(90deg, ${linearGradient})` }
}
})`
width: ${({ width }) => width};
height: 0.5em;
`
これで
export const StripeSample = () => <Stripe data={[0,1,0,1,1,0,0,1,1,1,0,0,0,1,1,1,1,0,1,1,0,1]}/>
こんな感じに描画出来る
更に縦にも並べる。
CSSは複数のbackgroundを与えられるmultiple backgroundの仕組みがあるので、これを使ってちょっとずつ並べていくことで縦に伸ばしていく
export const generateQrCss = (code) => {
const rowDivision = 100 / code.length
const backgroundImage = code.map((data) => {
const linearGradient = generateGradient(data)
return `linear-gradient(90deg, ${linearGradient})`
})
const backgroundSize = code.map(
(_, i) => `auto ${round(rowDivision * (i + 1))}`
)
return {
backgroundImage,
backgroundSize
}
}
export const Code = styled.div.attrs(({ code }) => {
return {
width: `${code.length / 4}em`,
height: `${code.length / 4}em`
}
})`
${({ code }) => generateQrCss(code)};
background-repeat: no-repeat;
width: ${({ width }) => width};
height: ${({ height }) => height};
`
ここでbackground-imageの他にbackground-sizeも指定することで、後ろに描画される画像をちょっとずつ大きくする手法で縦方向に描画していっている。
background-sizeの代わりにbackground-positionでも良いが、空白が生まれるケースがあるので、background-sizeの方がおすすめ出来る
export const QrSample = () => <Code code={[
[0,0,0,1],
[0,0,1,1],
[0,1,1,0],
[1,1,0,0]
]}/>
するとこんな感じでドット絵になる
できあがり
ここまでくればあとはドットなら何でも描画出来る。
今回は手頃なところでQRコードを今回は使った。
QRコードの計算そのものはnode-qrcodeを利用している。
なお、生成されるコードは長くなるし割と複雑出し、容量の少なさを考えればbase64だしスケールしたときの綺麗さをみればCSS GridやSVCでやったほうが良く、ブラウザ互換性で考えても全く優位性は無い。
Discussion