<template>
<div class="clip-img" :style="imgStyle">
<img :src="url" alt="" crossOrigin="anonymous" :style="imgStyle">
<canvas ref="canvas" :style="imgStyle" @mousedown="onmousedown" @mousemove="onmousemove"></canvas>
</div>
</template>
<script>
export default {
name:"clip-image",
props:{
max:{ // 缩放基准宽度或高度
type:Number,
default:400
}
},
data(){
return {
img:null,
url:null,
imgInfo: null, // 压缩前的信息
imgCInfo:null, // 压缩后的信息
clipInfo:null, // 压缩前的信息
clipCinfo:null, // 压缩后的信息
ctx:null, // 画板
pos:{x:0,y:0},
lock: "", // 锁住一个方向
boundary:null
}
},
computed:{
imgStyle(){
let imgCInfo = this.imgCInfo;
if(imgCInfo){
return {
width: `${imgCInfo.w}px`,
height: `${imgCInfo.h}px`
}
}
return {height:"0px",height:"0px",display:"none"};
}
},
async mounted(){
this.clear();
this.ctx = this.$refs.canvas.getContext("2d");
window.addEventListener("mouseup",this.onmouseup);
},
methods:{
init(src,bound){
this.setSrc(src)
this.setClip(bound);
},
// 外部调用
async setSrc(src){
let img = await this.loadImage(src,{crossOrigin:"anonymous"});
this.img = img;
this.setImg(img);
},
clear(){
let obj = {
img:null,
url:null,
imgInfo: null, // 压缩前的信息
imgCInfo:null, // 压缩后的信息
clipInfo:null, // 压缩前的信息
clipCinfo:null, // 压缩后的信息
ctx:null, // 画板
pos:{x:0,y:0},
lock: "", // 锁住一个方向
boundary:null
}
for(let i in obj){
this[i] = obj[i];
}
},
loadImage(url,attrs){
this.url = url;
let img = new Image();
img.src = url;
attrs = attrs || {};
for(let i in attrs){
img[i] = attrs[i];
}
return new Promise((resolve,reject)=>{
img.onload = function(){
resolve(img);
};
img.onerror = reject;
});
},
setImg(img){
this.img = img;
this.imgInfo = {
w:img.width,
h:img.height
}; // 压缩图的比例
let w,h,scale;
if(img.width > img.height){
w = this.max;
scale = w/img.width;
h = scale*img.height;
}else{
h = this.max;
scale = (h/img.height);
w = scale * img.width;
}
this.imgCInfo = {
w,
h,
scale
};
let canvas = this.$refs.canvas;
canvas.width = w;
canvas.height = h;
this.setClip();
},
setClip(clipInfo){
if(clipInfo){
this.clipInfo = {
w:clipInfo.width,
h:clipInfo.height
};
}
if(this.imgCInfo && this.clipInfo){
this.compressClip(this.imgCInfo,this.clipInfo);
this.fill();
}
},
compressClip(imgCInfo,clipInfo){
// 压缩缩放
let w,h,scale;
let imgR = imgCInfo.w/imgCInfo.h;
let clipR = clipInfo.w / clipInfo.h; if(imgR > clipR){
// 图片的宽度偏大
h = imgCInfo.h;
scale = h/clipInfo.h;
w = scale * clipInfo.w;
this.lock = "h";
}else{
// 图片的宽度偏小
w = imgCInfo.w;
scale = w/clipInfo.w;
h = scale * clipInfo.h;
this.lock = "w";
}
this.clipCinfo = {
w,
h,
scale
}
this.boundary = {
w:this.imgCInfo.w - this.clipCinfo.w,
h:this.imgCInfo.h - this.clipCinfo.h
};
this.pos = {
x:0,
y:0
};
},
onmouseup(){
this.mouse = null;
},
onmousedown(e){
this.mouse = {
x:e.offsetX,
y:e.offsetY
}
},
onmousemove(e){
if(this.mouse){
let x = e.offsetX - this.mouse.x ;
let y = e.offsetY - this.mouse.y;
if(this.lock == "h"){
x = this.pos.x + x;
if(x < 0){
x = 0;
}else if(x > this.boundary.w){
x = this.boundary.w
}
this.pos.x = x;
}else{
y = this.pos.y + y;
if(y < 0){
y = 0;
}else if(y > this.boundary.h){
y = this.boundary.h
}
this.pos.y = y;
}
this.mouse = {
x:e.offsetX,
y:e.offsetY
}
this.fill();
}
},
fill(){
let {w,h} = this.clipCinfo;
let {x,y} = this.pos;
let clipctx = this.ctx;
let imgCInfo = this.imgCInfo;
clipctx.clearRect(0, 0, imgCInfo.w, imgCInfo.h);
clipctx.beginPath();
clipctx.fillStyle = 'rgba(0,0,0,0.6)';
clipctx.strokeStyle = "green";
//遮罩层
clipctx.globalCompositeOperation = "source-over";
clipctx.fillRect(0, 0, imgCInfo.w, imgCInfo.h);
//画框
clipctx.globalCompositeOperation = 'destination-out';
clipctx.fillRect(x, y, w, h);
//描边
clipctx.globalCompositeOperation = "source-over";
clipctx.moveTo(x, y);
clipctx.lineTo(x + w, y);
clipctx.lineTo(x + w, y + h);
clipctx.lineTo(x, y + h);
clipctx.lineTo(x, y);
clipctx.stroke();
clipctx.closePath();
},
exportBase(){
// 导出图片 base64
let pos = this.pos;
let scale = this.imgCInfo.scale;
let sx = pos.x / scale;
let sy = pos.y / scale;
let swidth = parseInt(this.clipCinfo.w / scale);
let sheight = parseInt(this.clipCinfo.h / scale);
let canvas = document.createElement("canvas");
canvas.width = swidth;
canvas.height = sheight;
let ctx = canvas.getContext("2d");
ctx.drawImage(this.img,sx,sy,this.imgInfo.w,this.imgInfo.h,0,0,this.imgInfo.w,this.imgInfo.h);
return canvas.toDataURL("image/png");
},
dataURLtoFile(b64Data,filename){
filename = filename || "test.png";
let mime = "image/png";
var bstr = atob(b64Data.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''));
var n = bstr.length;
var u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
// 转换成file对象
return new File([u8arr], filename, {type:mime}); // 转换成成blob对象
// return new Blob([u8arr],{type:mime});
// return blob;
}
},
destroyed(){
window.removeEventListener("mouseup",this.onmouseup);
}
}
</script>
<style lang="scss" scoped>
.clip-img{
border: 1px solid red;
margin: 20px auto;
position: relative;
height: 0;
width: 0;
overflow: hidden;
canvas{
position: absolute;
left: 0;
top: 0;
z-index: 1;
cursor: move;
}
img{
position: relative;
z-index: 0;
}
}
</style>

  

使用:

<template>
<div class="clip-img">
<clipImage ref="clipImage"></clipImage>
<button @click="getImg">导出</button> <button @click="slide">切换</button>
<img :src="src" v-if="src" alt="" class="result">
</div>
</template>
<script>
import clipImage from "@/components/clip-image.vue";
export default {
name:"clip-image",
data(){
return {
src:""
}
},
components:{
clipImage
},
mounted(){
// setSrc
this.$refs.clipImage.init("xxx",{width:400,height:200});
},
methods:{
getImg(){
this.src = this.$refs.clipImage.exportBase();
console.log("截图成功")
},
slide(){
this.$refs.clipImage.setSrc("xxx");
}
}
}
</script>
<style lang="scss" scoped>
.clip-img{
.result{
max-width: 400px;
}
}
</style>

  

撸一个 vue 的截图组件,按比例截取的更多相关文章

  1. 手把手从零开始---封装一个vue视频播放器组件

    现在,在网页上播放视频已经越来越流行,但是网上的资料鱼龙混杂,很难找到自己想要的,今天小编就自己的亲身开发体验,手把手从零开始---封装一个vue视频播放器组件. 作为一个老道的前端搬砖师,怎么可能会 ...

  2. 纯手工撸一个vue框架

    前言 vue create 真的很方便,但是很多人欠缺的是手动撸一遍.有些人离开脚手架都不会开发了. Vue最简单的结构 步骤 搭建最基本的结构 打开空文件夹,通过 npm init 命令生成pack ...

  3. 从零开始徒手撸一个vue的toast弹窗组件

    相信普通的vue组件大家都会写,定义 -> 引入 -> 注册 -> 使用,行云流水,一气呵成,但是如果我们今天是要自定义一个弹窗组件呢? 首先,我们来分析一下弹窗组件的特性(需求): ...

  4. VUE -- 如何快速的写出一个Vue的icon组件?

    伴随着Vue的诞生,它似乎就被人寄予厚望,不仅仅是因为其轻量级的MVVM设计方式,而且其实现了组件化开发模式,所以越来越多的人会拿Vue和AngularJS.React Native做比较.具体关于它 ...

  5. 基于iview 封装一个vue 表格分页组件

    iview 是一个支持中大型项目的后台管理系统ui组件库,相对于一个后台管理系统的表格来说分页十分常见的 iview是一个基于vue的ui组件库,其中的iview-admin是一个已经为我们搭好的后天 ...

  6. 手把手教你实现一个 Vue 进度条组件!

    最近在个人的项目中,想对页面之间跳转的过程进行优化,想到了很多文档或 npm 等都用到的页面跳转进度条,于是便想自己去实现一个,特此记录. 来看下 npm 搜索组件时候的效果: so 下面咱们一起动手 ...

  7. 撸一个vue的双向绑定

    1.前言 说起双向绑定可能大家都会说:Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更 ...

  8. 一个vue的日历组件

    说明: 1.基于element-ui开发的vue日历组件. 地址 更新: 1.增加value-format指定返回值的格式2.增加头部插槽自定义头部 <ele-calendar > < ...

  9. 来吧,自己动手撸一个分布式ID生成器组件

    在经过了众多轮的面试之后,小林终于进入到了一家互联网公司的基础架构组,小林目前在公司有使用到架构组研究到分布式id生成器,前一阵子大概看了下其内部的实现,发现还是存在一些架构设计不合理之处.但是又由于 ...

随机推荐

  1. Unity3d 错误提示 GUI Error: You are pushing more GUIClips than you are popping. Make sure they are balanced

    程序出現這個問題的話,程序編譯時正確,運行時報錯,而且沒有報出是哪個代碼文件出處. 這個問題一般首先去檢查Level內有用到OnGUI,Debug結果發現某代碼文件在調試代碼時複製多了一行GUILay ...

  2. (转)AutoML 与轻量模型大列表: awesome-AutoML-and-Lightweight-Models

    Awesome-AutoML-and-Lightweight-Models 原文:http://bbs.cvmart.net/articles/414/zi-yuan-automl-yu-qing-l ...

  3. Tomcat7/8/8.5三种版本的redis-session-manager的jar和xml配置均不同

    chexagon/redis-session-manager: A tomcat8 session manager providing session replication via persiste ...

  4. jenkins关联shell命令修改pom项目版本

    #获取pom文件内的项目版本 version=`awk '/<version>[^<]+<\/version>/{gsub(/<version>|<\/ ...

  5. composer 安装以及使用教程

    CentOS 7 安装 Composer: composer 官方下载文档:https://getcomposer.org/download/ 首先 centos 必须安装 php-cli,也就是在命 ...

  6. Kotlin数据类型 Unit、Nothing与Nothing?、Any与Any?

    Kotlin数据类型 Unit.Nothing与Nothing?.Any与Any?   本文链接:https://blog.csdn.net/ldxlz224/article/details/9440 ...

  7. Python3基础 str : 对字符串进行切片

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  8. 【JAVA】java注解的自定义和使用

    java注解概念 Java提供了一种原程序中的元素关联任何信息和任何数据的途径和方法 java注解介绍 常用注解 @Override:表示方法是重写的方法 @Deprecated:过时的方法 @Sup ...

  9. 3D游戏引擎设计 实时计算机图形学的应用方法 第2版 pdf 带索引书签目录

    3D游戏引擎设计  实时计算机图形学的应用方法  第2版 目录 第1章 概述1.1 图形硬件和游戏发展史1.2 本书版本与软件发展史1.3 章节导读 第2章 图形系统2.1 基础知识2.1.1 坐标系 ...

  10. 【linux学习笔记五】帮助命令

    man //查看ls作用 man ls man -f命令 相当于 whatis命令 --help ls --help help help shell help cd info详细命令帮助