🐊
cropperjsで写真切り取りのコンポーネントを作る
cropperjsの概要や使い方ついては、こちらのサイトで紹介されております。
Nuxt3での使い方
- コンポーネントでのインポート
import Cropper from 'cropperjs';
ちなみにoptionsなどの型はCropper.Optionsでインストール可能
- スタイル設定
Cropperをインポートするコンポーネントと同じファイルに入れなければ反応しなかった。
(main.cssに入れたり、style scopedにするとダメ)
<style>
@import "cropperjs/dist/cropper.css";
</style>
使用例
クリックで展開
<script setup lang="ts">
import Cropper from 'cropperjs';
const {imageURL,overwrite,maxSize} = withDefaults(
defineProps<{imageURL?:string,overwrite?:boolean,maxSize?:number}>(),
{
overwrite:false,
maxSize:1000
}
)
const temp = ref<string>(imageURL?.startsWith('http')? imageURL:'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')
const croppedImage=ref<string>('') // base64URL
const croppedImageSize = computed(()=>Math.round(croppedImage.value.length/1.33/1000))
const cropper=ref<Cropper>()
var cropBoxData:Cropper.SetCropBoxDataOptions
const setCroppedImage=()=>{
if(!cropper.value)return
const croppedCanvas = cropper.value.getCroppedCanvas();
croppedImage.value = croppedCanvas.toDataURL('image/png');
}
const options:Cropper.Options={
aspectRatio: 1 / 1,
modal:false,
minCropBoxHeight:100,
minCropBoxWidth:100,
cropend:()=>{
setCroppedImage()
cropBoxData = cropper.value?.getCropBoxData()
},
ready:()=>{
if(cropBoxData){
cropper.value?.setCropBoxData(cropBoxData)
}
setCroppedImage()
}
}
onMounted(()=>{
if(imageURL?.startsWith('http'))return
const target = document.getElementById('target') as HTMLImageElement
cropper.value = new Cropper(target,options);
})
const 画質を下げる=(x:number)=>{ // numberは1から100まで
const imgElem = new Image();
imgElem.src = temp.value;
imgElem.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = imgElem.width;
canvas.height = imgElem.height;
const ctx = canvas.getContext('2d');
ctx?.drawImage(imgElem, 0, 0, imgElem.width, imgElem.height);
canvas.toBlob((blob) => {
if (!blob) return;
const reader = new FileReader();
reader.onloadend = () => {
cropper.value?.replace(reader.result as string)
}
reader.readAsDataURL(blob);
}, 'image/jpeg', x/100);
}
}
const setFile=(files:FileList|null)=>{
const reader = new FileReader()
reader.onload = (e)=>{
const dataURL = e.target?.result as string
temp.value=dataURL
cropper.value?.replace(dataURL)
}
if(!files||!files.length)return
const file = files.item(0) as File;
reader.readAsDataURL(file)
}
</script>
<template>
<div class=" space-y-4">
<div class="flex items-center justify-center h-72 overflow-hidden">
<img id="target" :src="temp" class="max-w-full max-h-full">
</div>
<div v-if="!imageURL?.startsWith('http')" class="w-full flex ">
<label>画質</label>
<input type="range" min="1" value="100" @change="画質を下げる(Number(($event.target as HTMLInputElement).value))"
class="flex-auto mx-4 "
>
<div :class="croppedImageSize>maxSize? 'text-red-500':''">{{ croppedImageSize }}kB</div>
</div>
<div v-if="croppedImageSize>maxSize" class="text-xs text-red-500">画質を落とすか、選択範囲を狭めて{{maxSize}}kB以内にしてください。</div>
<div class=" flex justify-between">
<label v-if="overwrite || !imageURL?.startsWith('http')">
<div class="button">選択</div>
<input type="file" accept="image/jpeg, image/png" class="hidden" @change="setFile(($event.target as HTMLInputElement).files)">
</label>
<slot :data="croppedImage" :size="croppedImageSize"></slot>
</div>
</div>
</template>
<style>
@import "cropperjs/dist/cropper.css";
</style>
解説
画質を落とす
画質を落とす機能は、imgエレメントとcanvasエレメントを使って実装できる。
canvasで画質を落とす際、toBlobのqualityに数値を設定すれば画質を落とせる。
同じ画像に繰り返し実行しても一度しか画質は落ちないので、一度画質を落としても満足できない場合はもとの画像からqualityにもっと低い数値を入れて画質を落としましょう。
cropper画像の入れ替え
cropper.replace(<base64data>)で、cropperオブジェクトの画像を入れ替えることができる。
その際、トリミングボックスのサイズと位置は初期化されてしまうので、事前にサイズと位置の状態情報は保存しておいて、セットしなおすと良い
cropper optionsのreadyについて
前述のreplaceは処理に少し時間がかかるため、この後にcropperオブジェクトに何か作業させるとcropperがundefined状態となりエラーとなることがある。
setTimeで100ミリ秒くらい置いても良いが、あまり美しくない。
readyに実行内容を書くと、cropperオブジェクトが生成終了した直後に実行させることができる。
Discussion