機能:複数のイメージを同時にアップロードしたり、各イメージを圧縮してアップロードしたり、イメージを表示し直したりすることができます。
基本的な考え方です:
1、アップロードされたInputのイメージオブジェクトファイルを取得します。
2、イメージをbase64形式に変換します。
3は、Canvasの変換圧縮を介してbase64エンコードされたイメージは、ここではCanvasのdrawImageとtoDataURL 2つのAPIを使用します、1つは、イメージの解像度を調整するために、1つは、イメージの圧縮と出力の品質を調整することです。
4、変換されたイメージは、対応する新しいイメージを生成し、出力します。
注目してください:
イメージサイズが縮小されるサイズの制限を超えています。
iosのキャンバスはイメージを描画する際に回転します。
イメージを選択し、入力ファイルのイメージオブジェクトは20M以下でなければなりません。
/**
* イメージを選択する
*/
selectImgs (e) {
var files = e.target.files
// ファイルの選択を解除し、何もしない。
if (files.length === 0) {
return
}
// アップロードするイメージの数+すでにアップロードされているイメージの数が4より大きい場合、プロンプトが送信される。
if (this.localPicArr.length + files.length > 4) {
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_pic_num_limit')
})
return
}
// アップロードされた写真のリストに写真でないものがあれば、プロンプトが表示される。
const notAllImage = Object.keys(files).some((file) => files[file].type.indexOf("image") !== 0)
if (notAllImage) {
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_pic_limit')
})
return
}
// アップロードされたイメージリストが20MBを超える場合、プロンプトが表示される。
const isTooLarge = Object.keys(files).some((file) => files[file].size > * 20)
if (isTooLarge) {
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_pic_size_limit')
})
return
}
// アップロードされたファイルがすべて条件を満たしていれば、イメージの読み込みを開始する。
Object.keys(files).some((file, index) => this.loadImages(files[file], index))
},
ファイルの読み込みを開始し、写真の向き属性を取得します。
/**
* イメージを読み込む
*/
loadImages (file, index) {
let orientation = null
//写真の向き角度属性取得、ユーザー回転コントロール
//写真の向き角度属性取得、ユーザー回転制御
// Orientation写真の角度
// DateTime撮影時間
// ImageWidth写真の幅
// ImageHeight写真の高さ
// イメージのデータを取得する
EXIF.getData(file, function() {
// イメージの全データを取得し、その値をオブジェクトとして返す。
EXIF.getAllTags(this)
// イメージのデータを取得する
orientation = EXIF.getTag(this, 'Orientation')
})
var that = this
var reader = null
var picIndex = this.localPicArr.length + index
reader = new window.FileReader()
reader.readAsDataURL(file)
// ファイルの読み込みを開始する。
reader.onloadstart = function () {
that.localPicArr.push('loading')
}
// ファイルの読み込みを終了し、成功しても失敗しても読み込みを終了する。
reader.onloadend = function () {
// 入力をクリアする
that.$refs.input.value = ''
}
// ファイルの読み込みに失敗した
reader.onerror = function () {
// 読み込みに失敗したイメージを削除する。
that.localPicArr.splice(picIndex, 1)
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_error')
})
}
// 読み込み成功
reader.onload = function (e) {
that.tempImageArr[picIndex] = new Image()
that.tempImageArr[picIndex].onload = function () {
setTimeout(() => that.compressImages(file, that.tempImageArr[picIndex].width, that.tempImageArr[picIndex].height, picIndex, orientation), 1000)
}
that.tempImageArr[picIndex].src= e.target.result
}
},
ファイルを読み込んで正常に読み込み、イメージを圧縮して描画し、アップロードします。
/**
* イメージを圧縮する
*/
compressImages (file, originWidth, originHeight, index, orientation) {
var that = this
// 最大サイズ制限
var maxWidth = 800, maxHeight = 800
// 対象サイズ
var targetWidth = originWidth, targetHeight = originHeight
// イメージサイズがサイズ制限を超える
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 横幅でサイズを制限する
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
//
this.cavasArr[index] = document.getElementById(`cavas${index}`)
this.contextArr[index] = this.cavasArr[index].getContext('2d')
// canvasイメージを拡大縮小する
this.cavasArr[index].width = targetWidth
this.cavasArr[index].height = targetHeight
// キャンバスをクリアする
this.contextArr[index].clearRect(0, 0, targetWidth, targetHeight)
this.contextArr[index].drawImage(this.tempImageArr[index], 0, 0, targetWidth, targetHeight)
// アップロードされたイメージが回転していたら、回転させて戻す。
if (orientation !== undefined && orientation != 1) {
switch (orientation) {
case 6:
this.contextArr[index].rotate(Math.PI / 2)
this.contextArr[index].drawImage(this.cavasArr[index], 0, -targetHeight, targetWidth, targetHeight)
break
case 3:
this.contextArr[index].rotate(Math.PI)
this.contextArr[index].drawImage(this.cavasArr[index], -targetWidth, -targetHeight, targetWidth, targetHeight)
case 8:
this.contextArr[index].rotate(3 * Math.PI / 2)
this.contextArr[index].drawImage(this.cavasArr[index], -targetWidth, 0, targetWidth, targetHeight)
break
}
}
// canvasブロブに変換してアップロードする
this.cavasArr[index].toBlob(function (blob) {
// blobオブジェクトを配列に格納する。
that.blobArr.push(blob)
// 圧縮されたイメージを使用して、キャッシュが大きくなりすぎないようにする。
that.localPicArr.splice(index, 1, that.cavasArr[index].toDataURL())
const params = {
blobArr: that.blobArr,
localPics: that.localPicArr
}
// 親コンポーネントにデータを渡す
that.$emit('setPic', params)
}, file.type || 'image/png')
}
コンポーネントの全コード:
<template>
<div class="cmpress-container">
<label>
<input ref="input" id="file" multiple class="upload-input" type="file" name="image" accept="image/*" @change="selectImgs($event)">
<img :class="{ oc: usingType === 'oc', bp: usingType === 'bp' }" src="../../assets/img/original_collection/register/upload_icon.png">
</label>
<!-- イメージを表示する>
<div
v-for="(item, index) in localPicArr"
:key="index"
:class="{ oc: usingType === 'oc', bp: usingType === 'bp' }"
class="pic-view"
>
<div v-if="item === 'loading'" class="loading"><div class="xz"></div></div>
<div v-else class="img-wrapper" :class="{ oc: usingType === 'oc', bp: usingType === 'bp' }">
<img :src="item.img_url ? item.img_url : item" class="pic-item">
<img @click="delPic(index)" src="../../assets/img/original_collection/register/del_icon.png" class="del-icon">
</div>
</div>
<!-- DrawImage キャンバスを隠す 関数:drawImage イメージを描画したら、toDataURLを使ってbase64エンコーディングに変換し、さらにblobに変換してバックエンドに渡す。 formDataを使ってリクエスト--。>
<canvas
v-for="(item, index) in cavasArr"
:key="index + 100"
:id="'cavas' + index"
class="cp-cavas"
></canvas>
</div>
</template>
<script>
import EXIF from 'exif-js'
export default {
props: {
usingType: {
type: String,
default: 'oc'
},
imgDatas: {
type: Array,
default: []
}
},
data () {
return {
// canvas
cavasArr: [undefined, undefined, undefined, undefined],
// canvas
contextArr: [undefined, undefined, undefined, undefined],
// 余分なイメージオブジェクトは、マウントされたメソッドでインスタンス化される。
tempImageArr: [undefined, undefined, undefined, undefined],
// 最大長4のblobオブジェクトの配列を格納する。
blobArr: [],
// ローカルイメージの配列を保存する。
localPicArr: []
}
},
methods: {
/**
* データが表示されたらイメージデータを設定する。
*/
setLocalImages () {
this.localPicArr = this.imgDatas.slice(0)
this.blobArr = this.imgDatas.slice(0)
},
/**
* イメージを削除する
*/
delPic (index) {
this.localPicArr.splice(index, 1)
this.blobArr.splice(index, 1)
this.tempImageArr.splice(index, 1, undefined)
const params = {
blobArr: this.blobArr,
localPics: this.localPicArr
}
// 親コンポーネントにデータを渡す
this.$emit('setPic', params)
},
/**
* イメージを選択する
*/
selectImgs (e) {
var files = e.target.files
// ファイルの選択を解除し、何もしない。
if (files.length === 0) {
return
}
// アップロードするイメージの数+すでにアップロードされているイメージの数が4より大きい場合、プロンプトが送信される。
if (this.localPicArr.length + files.length > 4) {
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_pic_num_limit')
})
return
}
// アップロードされた写真のリストに写真でないものがあれば、プロンプトが表示される。
const notAllImage = Object.keys(files).some((file) => files[file].type.indexOf("image") !== 0)
if (notAllImage) {
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_pic_limit')
})
return
}
// アップロードされたイメージリストが20MBを超える場合、プロンプトが表示される。
const isTooLarge = Object.keys(files).some((file) => files[file].size > * 20)
if (isTooLarge) {
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_pic_size_limit')
})
return
}
// アップロードされたファイルがすべて条件を満たしていれば、イメージの読み込みを開始する。
Object.keys(files).some((file, index) => this.loadImages(files[file], index))
},
/**
* イメージを読み込む
*/
loadImages (file, index) {
let orientation = null
//写真の向き角度属性取得、ユーザー回転制御
EXIF.getData(file, function() {
EXIF.getAllTags(this)
orientation = EXIF.getTag(this, 'Orientation')
})
var that = this
var reader = null
var picIndex = this.localPicArr.length + index
reader = new window.FileReader()
reader.readAsDataURL(file)
// ファイルの読み込みを開始する。
reader.onloadstart = function () {
that.localPicArr.push('loading')
}
// ファイルの読み込みを終了し、成功しても失敗しても読み込みを終了する。
reader.onloadend = function () {
// 入力をクリアする
that.$refs.input.value = ''
}
// ファイルの読み込みに失敗した
reader.onerror = function () {
// 読み込みに失敗したイメージを削除する。
that.localPicArr.splice(picIndex, 1)
this.$store.dispatch('set_popup_datas', {
type: 'h_tips',
msg: this.$t('message.upload_error')
})
}
// 読み込み成功
reader.onload = function (e) {
that.tempImageArr[picIndex] = new Image()
that.tempImageArr[picIndex].onload = function () {
setTimeout(() => that.compressImages(file, that.tempImageArr[picIndex].width, that.tempImageArr[picIndex].height, picIndex, orientation), 1000)
}
that.tempImageArr[picIndex].src= e.target.result
}
},
/**
* イメージを圧縮する
*/
compressImages (file, originWidth, originHeight, index, orientation) {
var that = this
// 最大サイズ制限
var maxWidth = 800, maxHeight = 800
// 対象サイズ
var targetWidth = originWidth, targetHeight = originHeight
// イメージサイズがサイズ制限を超える
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 横幅でサイズを制限する
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
//
this.cavasArr[index] = document.getElementById(`cavas${index}`)
this.contextArr[index] = this.cavasArr[index].getContext('2d')
// canvasイメージを拡大縮小する
this.cavasArr[index].width = targetWidth
this.cavasArr[index].height = targetHeight
// キャンバスをクリアする
this.contextArr[index].clearRect(0, 0, targetWidth, targetHeight)
this.contextArr[index].drawImage(this.tempImageArr[index], 0, 0, targetWidth, targetHeight)
// アップロードされたイメージが回転していたら、回転させて戻す。
if (orientation !== undefined && orientation != 1) {
switch (orientation) {
case 6:
this.contextArr[index].rotate(Math.PI / 2)
this.contextArr[index].drawImage(this.cavasArr[index], 0, -targetHeight, targetWidth, targetHeight)
break
case 3:
this.contextArr[index].rotate(Math.PI)
this.contextArr[index].drawImage(this.cavasArr[index], -targetWidth, -targetHeight, targetWidth, targetHeight)
case 8:
this.contextArr[index].rotate(3 * Math.PI / 2)
this.contextArr[index].drawImage(this.cavasArr[index], -targetWidth, 0, targetWidth, targetHeight)
break
}
}
// canvasブロブに変換してアップロードする
this.cavasArr[index].toBlob(function (blob) {
// blobオブジェクトを配列に格納する。
that.blobArr.push(blob)
// 圧縮されたイメージを使用して、キャッシュが大きくなりすぎないようにする。
that.localPicArr.splice(index, 1, that.cavasArr[index].toDataURL())
const params = {
blobArr: that.blobArr,
localPics: that.localPicArr
}
// 親コンポーネントにデータを渡す
that.$emit('setPic', params)
}, file.type || 'image/png')
}
}
}
</script>
<style lang="less" scoped>
@keyframes loading{
0%{
transform: rotate(0deg);
}
100%{
transform: rotate(360deg);
}
}
.cp-cavas {
display: none;
}
.cmpress-container {
width: 100%;
height: 100%;
font-size: .16rem;
margin-top: .3rem;
display: flex;
flex-direction: row;
justify-content: flex-start;
.upload-input {
display: none;
}
.oc {
width: 1rem;
height: 1rem;
}
.bp {
width: 1.12rem;
height: 1.12rem;
}
.pic-view {
position: relative;
&.oc {
width: 1rem;
height: 1rem;
margin-left: .1rem;
.del-icon {
width: .22rem;
height: .22rem;
}
}
&.bp {
width: 1.12rem;
height: 1.12rem;
margin-left: .2rem;
.del-icon {
width: .26rem;
height: .26rem;
}
}
.loading {
width: 100%;
height: 100%;
border-radius: .1rem;
background: rgba(0,0,0,.25);
display: flex;
justify-content: center;
align-items: center;
.xz {
width: .6rem;
height: .6rem;
border: .02rem solid;
border: .02rem solid;
border-radius: 50%;
border-color: #fff #fff transparent;
animation: loading 1s linear infinite;
}
}
.img-wrapper {
overflow: hidden;
display: flex;
align-items: center;
border-radius: .1rem;
&.oc {
width: 1rem;
height: 1rem;
}
&.bp {
width: 1.12rem;
height: 1.12rem;
}
.pic-item {
border-radius: .1rem;
width: 100%;
height: auto;
position: relative;
}
}
.del-icon {
position: absolute;
top: -.1rem;
right: -.1rem;
}
}
}
</style>