我是靠谱客的博主 朴实牛排,最近开发中收集的这篇文章主要介绍移动端写文章,表情,图片裁剪(gif裁剪),图片预览效果源码地址cropperjs对图片进行裁剪操作photoswipe实现对图片进行预览操作父组件HomeView.vue,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
效果
源码地址
cropperjs对图片进行裁剪操作
地址cropperjs
实现思路
- 拿到父组件传递过来要裁剪的数组
- mounted阶段对当前图片类型进行判断是否是gif图片,并执行不同操作 组件进行实例化,并保存在数据中
croppering() {
// 是否是gif图片
if (this.imageUrl[this.index].isGif) {
this.gifInit();
} else {
this.init();
}
},
- 普通图片操作
init() {
this.myCropper = new Cropper(this.$refs.image, {
viewMode: 2,
dragMode: 'crop',
initialAspectRatio: 1,
// aspectRatio: 1,
checkOrientation: false,
checkCrossOrigin: false,
guides: false,
center: false,
background: false,
autoCropArea: 0.8, // 裁剪框为图片大小的80%
zoomOnWheel: false,
movable: false,
rotatable: false,
scalable: false,
zoomOnTouch: false,
});
},
- 当点击next时,将当前图片进行裁剪输出,并传递给父组件,并判断是否还有需要裁剪图片
this.afterImg = this.myCropper.getCroppedCanvas({
imageSmoothingQuality: 'high',
}).toDataURL('image/jpeg');
this.index++;
/**
* 向父组件传递裁剪过后的值
* 判断是否还有数据,有的话再次调用croper进行实例化操作
*/
if (this.imageUrl.length > this.index) {
// 通知父组件保存值
this.$emit('imgCropped', { src: this.afterImg, id: this.imageUrl[this.index - 1].id });
this.nextImg();
} else {
this.$emit('imgCropped', { src: this.afterImg, id: this.imageUrl[this.index - 1].id });
this.$emit('closeCropper');
}
- 由于cropperjs不支持gif裁剪,所以需要引入libgif.js(拆分gif图片)和gif.js(合并gif图片),然后像普通图片进行裁剪操作
- gif图片裁剪初始化
gifInit() {
this.pre_load_gif(this.imageUrl[this.index].url);
},
- libgif.js拆分gif,并将图片保存与img_list中
dataURLtoFile(dataurl, filename) {
const arr = dataurl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
},
// 将canvas转换成file对象
convertCanvasToImage(canvas, filename) {
return this.dataURLtoFile(canvas.toDataURL('image/png'), filename);
},
base64ToBlob(base64) {
const parts = base64.split(';base64,');
const contentType = parts[0].split(':')[1];
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; i += 1) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: contentType });
},
pre_load_gif(gif_source) {
// var img_list = [];
const gifImg = document.createElement('img');
// 转换成blob类型
const gif = this.base64ToBlob(gif_source);
// gif库需要img标签配置下面两个属性
gifImg.setAttribute('rel:animated_src', URL.createObjectURL(gif));
gifImg.setAttribute('rel:auto_play', '1');
document.body.appendChild(gifImg);
// console.log(gifImg);
// 新建gif实例
const rub = new SuperGif({ gif: gifImg });
rub.load(() => {
const img_list = [];
for (let i = 1; i <= rub.get_length(); i++) {
// 遍历gif实例的每一帧
rub.move_to(i);
// 将每一帧的canvas转换成file对象
const cur_file = this.convertCanvasToImage(rub.get_canvas(), `'test'-${i}`);
img_list.push({
file_name: cur_file.name,
url: URL.createObjectURL(cur_file),
file: cur_file,
});
}
this.img_list = img_list;
this.gifCropper();
});
gifImg.remove();
},
- 按照之前思路对每一帧图片进行裁剪,图片太多,自动裁剪
gifCropper() {
this.showItemImg = this.img_list[this.img_list_index].url;
this.$nextTick(() => {
this.myCropper = new Cropper(this.$refs.image, {
viewMode: 1,
dragMode: 'none',
checkOrientation: false,
checkCrossOrigin: false,
guides: false,
center: false,
background: false,
autoCropArea: 1,
zoomOnWheel: false,
movable: false,
rotatable: false,
scalable: false,
zoomOnTouch: false, // 允许缩放图片
ready: (e) => {
// 开始剪裁
this.sureSavaGif(e.srcElement.width);
},
});
});
},
sureSavaGif(imgWidth) {
if (imgWidth > 800) {
// eslint-disable-next-line no-param-reassign
imgWidth = 800;
}
const afterImg = this.myCropper.getCroppedCanvas({
imageSmoothingQuality: 'height',
width: imgWidth,
}).toDataURL('image/jpeg');
this.eachGif.push(afterImg);
// 销毁实例
this.myCropper.destroy();
// 判断gif是否每一帧都裁剪完毕
this.img_list_index++;
if (this.img_list.length > this.img_list_index) {
this.$nextTick(() => {
this.gifCropper();
});
} else {
// this.img_list_index = 0;
// this.eachGif = [];
this.mergeGif();
}
},
- 合并gif
// gif合并
async mergeGif() {
// const width=300;
// const height=300;
const gif = new GIF({
workers: 2,
quality: 10,
// width,
// height,
workerScript: getGifWorker(), // 自定义worker地址
});
let j = 0;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
for (let i = 1; i <= this.eachGif.length; i++) {
// eslint-disable-next-line no-await-in-loop
const imgImage = await this.loading(i);
canvas.width = imgImage.width;
canvas.height = imgImage.height;
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imgImage, 0, 0, canvas.width, canvas.height);
gif.addFrame(canvas, { copy: true, delay: 50 });
j++;
if (j >= this.eachGif.length) {
gif.render();
}
}
gif.on('finished', async (blob) => {
const result = await this.blobToBase64(blob);
console.log(result);
console.log(this.index);
this.$emit('imgCropped', { src: result, id: this.imageUrl[this.index].id });
// 生成图片链接
// const url = URL.createObjectURL(blob);
// console.log(url);
// const a = document.createElement('a');
// a.href = URL.createObjectURL(blob);
// a.download = 'test.gif';
// a.click();
gif.abort();
const gifsNode = document.getElementsByClassName('jsgif');
console.log(gifsNode[0]);
gifsNode[0].remove();
// 是否还要裁剪
this.index++;
console.log(this.imageUrl);
if (this.imageUrl.length > this.index) {
// eslint-disable-next-line no-unused-expressions
this.eachGif = [];
this.img_list_index = 0;
this.croppering();
} else {
this.index = 0;
this.img_list_index = 0;
// this.$refs.input.value = '';
this.$emit('closeCropper');
}
});
},
loading(i) {
return new Promise((resolve) => {
const imgImage = new Image();
imgImage.src = this.eachGif[i - 1];
document.body.appendChild(imgImage);
// console.log(imgImage);
imgImage.onload = () => {
resolve(imgImage);
imgImage.parentNode.removeChild(imgImage);
};
});
},
blobToBase64(blob) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = (e) => {
resolve(e.target.result);
};
// readAsDataURL
fileReader.readAsDataURL(blob);
fileReader.onerror = () => {
reject(new Error('blobToBase64 error'));
};
});
},
- 点击关闭执行关闭裁剪器
goBack() {
// 关闭剪裁器
this.$emit('closeCropper');
},
photoswipe实现对图片进行预览操作
地址photoswipe
实现思路
- 需要父组件传递三个参数
- 是否在预览,是执行预览操作开关
- 预览索引,初始时,也就是预览组件实例化后看到的时那一张图片
- 预览的数组,包含所有的img
props: {
isPrview: {
type: Boolean,
}, // 是否在预览(也是触发预览的开关)
previewIndex: {
type: Number,
}, // 预览索引
previewImg: {
type: Array,
}, // 预览img
},
- 监听isPrview,为true时,执行预览初始化
watch: {
isPrview(val) {
if (val === true) this.initPhotoSwiper();
},
},
- 预览初始化
initPhotoSwiper() {
/**
* 结构出来DOM元素
*/
const { pswp } = this.$refs;
const options = {
index: this.previewIndex, // 初始化预览索引,也就是显示数组中第几张图片
};
this.gallery = new PhotoSwiper(pswp, UI, this.previewImg, options);
// 实例化
this.gallery.init();
/**
* 关闭按钮
* 过滤调没选中的
*/
this.gallery.listen('close', () => {
const info = this.previewImg.filter((item) => item.completed).map((item) => ({
src: item.src,
id: item.id,
}));
console.log(info);
this.$emit('changImageUrl', info);
});
/**
* 点击下一张时获取对应状态
*/
this.gallery.listen('beforeChange', () => {
/**
* this.gallery.getCurrentIndex() 会拿到当前图片索引
* @type {*|boolean}
*/
this.isactive = this.previewImg[this.gallery.getCurrentIndex()].completed;
});
},
- 当点击自定义按钮时,切换选中状态
checkboxClick() {
/**
* 切换是否选中状态
*/
// eslint-disable-next-line max-len,vue/no-mutating-props
this.previewImg[this.gallery.getCurrentIndex()].completed = !this.previewImg[this.gallery.getCurrentIndex()].completed;
this.isactive = this.previewImg[this.gallery.getCurrentIndex()].completed;
},
- 关闭预览时,根据数组中每个图片的completed(true为选中)对数组进行过滤操作
父组件HomeView.vue
- 上传图片后,读取文件是异步操作,需要通过Promise进行同步处理
syncFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
resolve(e);
};
reader.onerror = () => {
reject();
};
});
},
- 读取文件后暂时保存到imageUrl数组中,并启用裁剪器对图片进行裁剪操作
async imgChange(e) {
const { files } = e.target;
for (let i = 0; i < files.length; i++) {
// const reader = new FileReader();
// reader.readAsDataURL(files[i]);
// console.log(files[i]);
// eslint-disable-next-line no-await-in-loop
const rederUrl = await this.syncFile(files[i]);
console.log(rederUrl);
if (this.imageUrl.length === 0) {
this.imageUrl.push({
id: rederUrl.loaded,
url: rederUrl.target.result,
});
} else {
const status = this.imageUrl.some((item) => item.id === rederUrl.loaded);
if (!status) {
this.imageUrl.push({
url: rederUrl.target.result,
id: rederUrl.loaded,
});
}
}
}
// 准备裁剪
this.showCropper = true;
},
- 因为cropperjs不支持gif裁剪,所以要在信息中加入是否是gif图片,并执行不同裁剪操作
// 拿到图片后缀名
const result = files[i].name.split('.')[1];
if (result === 'gif') {
this.isGif = true;
} else {
this.isGif = false;
}
- 裁剪后的图片保存于perImg,当perImg数组中有文件的话,将其渲染到页面上提供小图预览
imgCropped(data) {
this.perImg.push({
src: data.src,
id: data.id,
});
},
- 当点击图片,传入点击图片索引,并对图片的数据结构进行处理,再调起预览组件
this.$refs.img.forEach((item, index) => {
this.perImg[index].w = item.offsetWidth;
this.perImg[index].h = item.offsetHeight;
this.perImg[index].completed = true;
});
this.previewIndex = index;
this.isPrview = true;
})
- 在预览时可能执行取消图片操作,所以在关闭预览,对perImg重新赋值
changImageUrl(data) {
this.perImg = data;
this.isPrview = false;
}
最后
以上就是朴实牛排为你收集整理的移动端写文章,表情,图片裁剪(gif裁剪),图片预览效果源码地址cropperjs对图片进行裁剪操作photoswipe实现对图片进行预览操作父组件HomeView.vue的全部内容,希望文章能够帮你解决移动端写文章,表情,图片裁剪(gif裁剪),图片预览效果源码地址cropperjs对图片进行裁剪操作photoswipe实现对图片进行预览操作父组件HomeView.vue所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复