🔥
konvajsでobject-fitを実現するにはどうするか
こんにちは。
最近canvas(konvajs)にハマっているckoshien(シーこうしえん)です。
経緯
「キャップ野球情報局」というWEBサービスを作っているのですが、
選手の写真をフレームで飾って選手カード風味にするということをしています。
ただ、この実装、CSSで
borderBottomLeftRadius:'880px 400px'
画像のふちを丸めているのですが、どうもとてつもなく重いらしく、
度々丸めたところが灰色になって描画が追いつかないという問題がありました。
じゃあcanvasで書こう
canvasを使うためにkonvajsとreact-konva
を使います。
当然のことながら、konvaにはCSSで実装されていたobject-fit(画像を要素に合わせて自動的にリサイズするプロパティ)が使えません。
CSS | konvajs | |
---|---|---|
描画 | 重い | 軽い |
object-fit | ○ | ✖️ |
konvajsではどう実現するか
RectのfillPatternImage
で画像を描画します。
ただ、それだけではobject-fitは実現できません。
まず、リサイズ前の幅と高さ、リサイズ後の幅と高さの倍率を計算します。
アスペクト比を崩さないために、倍率の大きい方をそれぞれ幅と高さに乗算します。
それから、画像の中央が要素の中央に来るようにオフセットを計算します。
これでほぼobject-fitが実現できます。
const BackGroundImage:React.FC<{cornerRadius:number[], x:number, y:number, width:number, height:number, image:string, strokeColor?:string}> = ({ image, x, y, width, height, cornerRadius, strokeColor }) => {
const [imgElement] = useImage(image);
if(!imgElement){
return <></>
}
let scaleX = 1;
let scaleY = 1;
let offsetX = 0;
let offsetY = 0;
scaleX = width/imgElement.width;
scaleY = height/imgElement.height;
if(scaleY > scaleX){
offsetX = (imgElement.width * Math.max(scaleX,scaleY) - width)/2;
}else{
offsetY = (imgElement.height * Math.max(scaleX,scaleY) - height)/2;
}
offsetX, offsetY);
return <Rect
fillPatternImage={imgElement}
x={x} y={y} width={width} height={height}
cornerRadius={cornerRadius}
stroke={strokeColor}
strokeWidth={2}
fillPatternScale={{
x: Math.max(scaleX,scaleY),
y: Math.max(scaleX,scaleY)
}}
fillPatternOffset={{
x: offsetX,
y: offsetY
}}
/>;
};
chroma.jsなどでグラデーションを加えたり、Starコンポーネントも使って
以前のノッペリした印象を軽減したのがこちらです。
Discussion