Merge remote-tracking branch 'upstream/main'

This commit is contained in:
2026-08-26 09:31:34 +08:00
parent 9b8c4bfab6
commit 394eb0285d
34137 changed files with 3589424 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 goodboy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+599
View File
@@ -0,0 +1,599 @@
## 需要使用外层容器包裹并设置宽高
## 使用 注意: 需要关掉本地的mock服务, 不然图片转化会报错
```
组件内使用
import { VueCropper } from 'vue-cropper'
components: {
VueCropper,
},
main.js里面使用
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
cdn方式使用
<script src="//cdn.jsdelivr.net/npm/vue-cropper@0.4.9/dist/index.js"></script>
Vue.use(window['vue-cropper'].default)
nuxt 使用方式
if(process.browser) {
vueCropper = require('vue-cropper')
Vue.use(vueCropper.default)
}
```
``` html
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
></vueCropper>
```
## vue-cropper
### 一个优雅的图片裁剪插件
[预览](http://xyxiao.cn/vue-cropper/example/)
[english](https://github.com/xyxiao001/vue-cropper/blob/master/english.md)
### vue-cropper 相关文章参考。
#### [shn_ui - vue-cropper ](https://shnhz.github.io/shn-ui/#/component/vue-cropper) 作者 野宁新之助
#### [vue全家桶开发管理后台—裁切图片](https://blog.csdn.net/qq_30632003/article/details/79639346) 作者: 麻球科技-菅双鹏
#### [Vue-cropper 图片裁剪的基本原理](https://www.cnblogs.com/tugenhua0707/p/8859291.html) 作者: 龙恩0707
#### [关于昵称和头像的总结(模仿微信)](https://zhuanlan.zhihu.com/p/45746753) 作者: 秋晨光
#### [vue-cropper-h5](https://github.com/2277419213/vue-cropper-h5) 作者:居里栈栈
### vue-cropper 交流。
##### 有什么意见,或者bug 或者想一起开发vue-cropper, 或者想一起开发其他插件
![](https://qn-qn-kibey-static-cdn.app-echo.com/4C6FE9E2-3D06-402B-8F32-98B82BEBDD9F.png)
# vue-cropper
#### 安装
```
npm install vue-cropper
yarn add vue-cropper
```
### 如果你没有使用npm
[在线例子](https://codepen.io/xyxiao001/pen/wxwKGz)
### 服务器渲染 nuxt 解决方案 设置为ssr: false
```
module.exports = {
...
build: {
vendor: [
'vue-cropper
...
plugins: [
{ src: '~/plugins/vue-cropper', ssr: false }
]
}
}
```
### 目前还不知道什么原因项目里面开启mock 会导致file报错, 建议使用时 关掉mock
<table style="text-align: center">
<thead>
<tr>
<td>名称</td>
<td>功能</td>
<td>默认值</td>
<td>可选值</td>
</tr>
</thead>
<tbody>
<tr>
<td>img</td>
<td>裁剪图片的地址</td>
<td>空</td>
<td>url 地址 || base64 || blob</td>
</tr>
<tr>
<td>outputSize</td>
<td>裁剪生成图片的质量</td>
<td>1</td>
<td>0.1 - 1</td>
</tr>
<tr>
<td>outputType</td>
<td>裁剪生成图片的格式</td>
<td>jpg (jpg 需要传入jpeg)</td>
<td>jpeg || png || webp</td>
</tr>
<tr>
<td>info</td>
<td>裁剪框的大小信息</td>
<td>true</td>
<td>true || false</td>
</tr>
<tr>
<td>canScale</td>
<td>图片是否允许滚轮缩放</td>
<td>true</td>
<td>true || false</td>
</tr>
<tr>
<td>autoCrop</td>
<td>是否默认生成截图框</td>
<td>false</td>
<td>true || false</td>
</tr>
<tr>
<td>autoCropWidth</td>
<td>默认生成截图框宽度</td>
<td>容器的80%</td>
<td>0~max</td>
</tr>
<tr>
<td>autoCropHeight</td>
<td>默认生成截图框高度</td>
<td>容器的80%</td>
<td>0~max</td>
</tr>
<tr>
<td>fixed</td>
<td>是否开启截图框宽高固定比例</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>fixedNumber</td>
<td>截图框的宽高比例</td>
<td>[1, 1]</td>
<td>[宽度, 高度]</td>
</tr>
<tr>
<td>full</td>
<td>是否输出原图比例的截图</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>fixedBox</td>
<td>固定截图框大小 不允许改变</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>canMove</td>
<td>上传图片是否可以移动</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>canMoveBox</td>
<td>截图框能否拖动</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>original</td>
<td>上传图片按照原始比例渲染</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>centerBox</td>
<td>截图框是否被限制在图片里面</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>high</td>
<td>是否按照设备的dpr 输出等比例图片</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>infoTrue</td>
<td>true 为展示真实输出图片宽高 false 展示看到的截图框宽高</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>maxImgSize</td>
<td>限制图片最大宽度和高度</td>
<td>2000</td>
<td>0-max</td>
</tr>
<tr>
<td>enlarge</td>
<td>图片根据截图框输出比例倍数</td>
<td>1</td>
<td>0-max(建议不要太大不然会卡死的呢)</td>
</tr>
<tr>
<td>mode</td>
<td>图片默认渲染方式</td>
<td>contain</td>
<td>contain , cover, 100px, 100% auto</td>
</tr>
<!-- <tr>
<td>limitMinSize</td>
<td>截图框最小宽高</td>
<td>10</td>
<td>number, string, [number, number]</td>
</tr> -->
</tbody>
</table>
### 内置方法 通过this.$refs.cropper 调用
##### this.$refs.cropper.startCrop() 开始截图
##### this.$refs.cropper.stopCrop() 停止截图
##### this.$refs.cropper.clearCrop() 清除截图
##### this.$refs.cropper.changeScale() 修改图片大小 正数为变大 负数变小
##### this.$refs.cropper.getImgAxis() 获取图片基于容器的坐标点
##### this.$refs.cropper.getCropAxis() 获取截图框基于容器的坐标点
##### this.$refs.cropper.goAutoCrop 自动生成截图框函数
##### this.$refs.cropper.rotateRight() 向右边旋转90度
##### this.$refs.cropper.rotateLeft() 向左边旋转90度
#### 图片加载的回调 imgLoad 返回结果success, error
#### 获取截图信息
this.$refs.cropper.cropW 截图框宽度
this.$refs.cropper.cropH 截图框高度
``` js
// 获取截图的base64 数据
this.$refs.cropper.getCropData((data) => {
// do something
console.log(data)
})
// 获取截图的blob数据
this.$refs.cropper.getCropBlob((data) => {
// do something
console.log(data)
})
### Description of the default rendering mode of the image
Image layout mode mode achieves the same effect as css background
Contain Centered layout Default does not scale Ensure the image is inside the container mode: 'contain'
Cover stretch layout fill the entire container mode: 'cover'
If only one value is given, this value will be used as the width value and the height value will be set to auto. mode: '50px'
If two values are given, the first one will be the width value and the second will be the height value. mode: '50px 60px'
### 预览
``` html
@realTime="realTime"
// Real time preview function
realTime(data) {
var previews = data;
var h = 0.5;
var w = 0.2;
this.previewStyle1 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: h
};
this.previewStyle2 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: w
};
固定为100宽度
this.previewStyle3 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: 100 / preview.w
};
固定为100高度
this.previewStyle4 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: 100 / preview.h
};
this.previews = data;
},
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="option.img" :style="previews.img">
</div>
</div>
<p>中等大小</p>
<div :style="previewStyle1">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<p>迷你大小</p>
<div :style="previewStyle2">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
=
```
#### 图片移动回调函数 @imgMoving
```
data type
{
moving: true, // moving 是否在移动
axis: {
x1: 1, // 左上角
x2: 1// 右上角
y1: 1// 左下角
y2: 1 // 右下角
}
}
```
#### 截图框移动回调函数 @cropMoving
```
data type
{
moving: true, // moving 是否在移动
axis: {
x1: 1, // 左上角
x2: 1// 右上角
y1: 1// 左下角
y2: 1 // 右下角
}
}
```
## 更新日志
### 0.55
```
修复 ios 版本小于 13.4没有处理图片旋转的 bug
```
### 0.54
```
去除 log 信息
修复 pc safari低版本问题
```
### 0.53
```
因为 chrome81 内核版本和 ios13.5 版本修复了图片旋转的 bug
插件在新版浏览器默认不处理旋转了,低版本浏览器自动处理
https://www.chromestatus.com/feature/6313474512650240
```
### 0.52
```
撤回最小弹框属性, 存在弹框拖拽坐标判断的 bug
```
### 0.51
```
更新裁剪框最小属性, 限制最小区域,可传1以上的数字和字符串,限制长宽都是这么大 也可以传数组[90,90]
limitMinSize: [Number, Array, String]
```
### 0.50
支持图片清空
修复 ie11 ie10 不能使用问题
修复 URL.createObjectURL 创建后没有销毁的 bug
添加截图框修改触发事件
this.$emit('change-crop-size', {
width: this.cropW,
height: this.cropH
})
### 0.49
修复滚轮默认事件问题
修复html静态文件引入事件触发问题
### 0.48
修复mode 属性 contain 和cover的显示bug问题
修复ios 下面base64图片跨域显示问题
### 0.47
修复第一次不触发预览的问题
新增加图片渲染mode功能
### 0.46
修复图片旋转bug
修复显示的一些bug
### 0.45
添加倍数使用 enlarge
可以输出裁剪框等比例图片;
感谢来自于 [](https://github.com/hzsrc)的贡献
添加预览框各种比例, 和修复图片截图小数问题
### 0.44
修复引入方式的问题
```
组件内使用
import { VueCropper } from vue-cropper
components: {
VueCropper,
},
main.js里面使用
import VueCropper from vue-cropper
Vue.use(vueCropper)
cdn方式使用
<script src="vuecropper.js"></script>
Vue.use(window['vue-cropper'])
```
### v0.43
剥离exif的依赖库, 添加exfi-min.js减小代码体积 45.9k => 37k
build 升级webpack4 升级
```
添加vue install 方法 =》 npm: Vue.use(VueCropper) web: Vue.use(window['vue-cropper'])
```
### v0.42
修复截图框因为去除小数点的引起的问题
### v0.41
修复截图框边界问题
### v0.40
修复orientation的处理方式
感谢 [Felipe Mengatto](https://github.com/felipemengatto)的贡献
### v0.39
修复orientation值不同带来的问题
感谢 [Felipe Mengatto](https://github.com/felipemengatto)的贡献
### v0.38
```
修改坐标反馈问题
```
### v0.37
```
修复centerBox 的截图超出1px问题
添加截图 图片移动触发事件
```
### v0.36
```
修复旋转自动生成截图框的错误
修改autocrop 可以动态生成截图框
```
### v0.35
```
修复其他图片没有压缩的问题
```
### v0.34
```提供移动端崩溃的解决方案
修改maxImgSize为2000
```
### v0.33
```提供移动端崩溃的解决方案
maxImgSize 限制图片最大宽度和高度 默认为2000px
```
### v0.32
```
新增截图框信息展示
infoTrue true 为展示真实输出图片宽高 false 展示看到的截图框宽高
```
### v0.30
```
新增获取图片坐标函数 this.$refs.cropper.getImgAxis()
新增获取截图框坐标函数 this.$refs.cropper.getCropAxis()
新增对高清设备的兼容 high
新增截图框限制在图片以内的功能 centerbox
新增自动生成截图框函数 this.$refs.cropper.goAutoCrop
```
### v0.29
新增图片加载的回调 imgLoad 返回结果success, error
### v0.28
修复截图框固定 截图框会影响原图移动 缩放
### v0.27
鼠标缩放问题优化
img max-width 样式优化
新增属性
canMove 是否可以移动图片 默认为是
canMoveBox 是否可以移动截图框 默认为是
original 是否按图片原始比例渲染 默认为否
### v0.26
修复火狐浏览器 鼠标缩放问题
### v0.25
修复图片有可能不展示
### v0.24
修复ios拍照旋转 截图问题 添加自动修复图片 截图预览代码变更, 修改默认上传图片为blob预览
``` html
realTime (data) {
this.previews = data
}
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
```
### v0.23
小优化
### v0.22
新增修改图片大小函数 通过this.$refs.cropper.changeScale 调用
### v0.21
新增固定截图框大小fiexdBox(注: 最好搭配自动生成截图框使用)
### v0.20
新增输出原图比例截图 props名full, 修复缩放图片过大灵敏度问题
### v0.19
新增图片旋转 修复mac滚轮过度灵敏
``` js
this.$refs.cropper.rotateRight() // 向右边旋转90度
this.$refs.cropper.rotateLeft() // 向左边旋转90度
```
### v0.18
修复默认生成截图框超过容器错误
### v0.17
修复blob数据获取错误
### v0.15
添加手机端手势缩放
```
canScale: true
```
### v0.13
添加预览
``` html
@realTime="realTime"
// Real time preview function
realTime (data) {
this.previews = data
}
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="option.img" :style="previews.img">
</div>
</div>
```
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sources":["webpack://vue-cropper/webpack/universalModuleDefinition"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"vue-cropper\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"vue-cropper\"] = factory();\n\telse\n\t\troot[\"vue-cropper\"] = factory();\n})(window, function() {\nreturn "],"mappings":"AAAA","sourceRoot":""}
+544
View File
@@ -0,0 +1,544 @@
## vue-cropper
### A simple picture clipping plugin for vue
[preview](http://xyxiao.cn/vue-cropper/example/)
[中文](https://github.com/xyxiao001/vue-cropper)
### Vue-cropper Related Articles Reference.。
#### [vue全家桶开发管理后台—裁切图片](https://blog.csdn.net/qq_30632003/article/details/79639346)   作者: 麻球科技-菅双鹏
#### [Vue-cropper 图片裁剪的基本原理](https://www.cnblogs.com/tugenhua0707/p/8859291.html) 作者: 龙恩0707
### vue-cropper communication.。
##### Any comments, or bugs or want to develop vue-cropper together, or want to develop other plugins together
![](https://qn-qn-kibey-static-cdn.app-echo.com/4C6FE9E2-3D06-402B-8F32-98B82BEBDD9F.png)
# vue-cropper
#### Install
```
npm install vue-cropper
yarn add vue-cropper
```
#### Use
```
views
import { VueCropper } from "vue-cropper"
components: {
VueCropper,
},
main.js
import VueCropper from "vue-cropper"
Vue.use(VueCropper)
cdn
<script src="vuecropper.js"></script>
Vue.use(window['vue-cropper'])
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
></vueCropper>
```
### not use npm or webpack
[online example](https://codepen.io/xyxiao001/pen/wxwKGz)
### serve render nuxt, control: ssr: false
```
module.exports = {
...
build: {
vendor: [
'vue-cropper
...
plugins: [
{ src: '~/plugins/vue-cropper', ssr: false }
]
}
}
```
<table style="text-align: center">
<thead>
<tr>
<td>name</td>
<td>Features</td>
<td>Detail</td>
<td>value</td>
</tr>
</thead>
<tbody>
<tr>
<td>img</td>
<td>Picture address</td>
<td>null</td>
<td>url address || base64 || blob</td>
</tr>
<tr>
<td>outputSize</td>
<td>Crop the quality of the generated image</td>
<td>1</td>
<td>0.1 - 1</td>
</tr>
<tr>
<td>outputType</td>
<td>Crop the format of the generated image</td>
<td>jpg (jpg need jpeg)</td>
<td>jpeg || png || webp</td>
</tr>
<tr>
<td>info</td>
<td>Crop box size information</td>
<td>true</td>
<td>true || false</td>
</tr>
<tr>
<td>canScale</td>
<td>Whether the image allows the wheel to zoom</td>
<td>true</td>
<td>true || false</td>
</tr>
<tr>
<td>autoCrop</td>
<td>Whether to generate a screenshot box by default</td>
<td>false</td>
<td>true || false</td>
</tr>
<tr>
<td>autoCropWidth</td>
<td>Default generation of screenshot frame width</td>
<td>parent's 80%</td>
<td>0~max</td>
</tr>
<tr>
<td>autoCropHeight</td>
<td>Default generation of screenshot frame Height</td>
<td>parent's 80%</td>
<td>0~max</td>
</tr>
<tr>
<td>fixed</td>
<td>Whether to open the screenshot frame width and height fixed ratio</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>fixedNumber</td>
<td>The aspect ratio of the screenshot box</td>
<td>[1 : 1]</td>
<td>[width : height]</td>
</tr>
<tr>
<td>full</td>
<td>Screenshot of whether to output the original map scale</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>fixedBox</td>
<td>Fixed screenshot frame size is not allowed to change</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>canMove</td>
<td>Whether the uploaded image can be moved</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>canMoveBox</td>
<td>Can the screenshot box be dragged?</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>original</td>
<td>Upload images are rendered in raw scale</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>centerBox</td>
<td>Is the screenshot box restricted to the image?</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>high</td>
<td>Is it proportional to the dpi output of the device?</td>
<td>true</td>
<td>true | false</td>
</tr>
<tr>
<td>infoTrue</td>
<td>True to show the true output image width and height false show the width of the screenshot frame</td>
<td>false</td>
<td>true | false</td>
</tr>
<tr>
<td>maxImgSize</td>
<td>Limit the maximum width and height of the image</td>
<td>2000</td>
<td>0-max</td>
</tr>
<tr>
<td>enlarge</td>
<td>Picture output ratio multiplier based on screenshots</td>
<td>1</td>
<td>0-max(Don't be too big.)</td>
</tr>
<tr>
<td>mode</td>
<td>img render mode</td>
<td>contain</td>
<td>contain , cover, 100px, 100% auto</td>
</tr>
<!-- <tr>
<td>limitMinSize</td>
<td>crop mini width height</td>
<td>10</td>
<td>number, string, [number, number]</td>
</tr> -->
</tbody>
</table>
### Built-in method Called by this.$refs.cropper
##### this.$refs.cropper.startCrop() Start the screenshot
##### this.$refs.cropper.stopCrop() Stop the screenshot
##### this.$refs.cropper.clearCrop() Clear screenshot
##### this.$refs.cropper.changeScale() Modify the image size. The positive number becomes larger. The negative number becomes smaller.
##### this.$refs.cropper.getImgAxis() Get the image based on the container's coordinate points
##### this.$refs.cropper.getCropAxis() Get the screenshot box based on the container's coordinate point
##### this.$refs.cropper.goAutoCrop Automatically generate screenshot box functions
##### this.$refs.cropper.rotateRight() Rotate 90 degrees to the right
##### this.$refs.cropper.rotateLeft() Rotate 90 degrees to the left
#### Image loaded callback imgLoad returns the result success, error
#### Get screenshot information
this.$refs.cropper.cropW screenshot frame width
this.$refs.cropper.cropH screenshot frame height
``` js
// Get the base64 data of the screenshot
this.$refs.cropper.getCropData((data) => {
   // do something
   Console.log(data)
})
// Get the screenshot of the blob data
this.$refs.cropper.getCropBlob((data) => {
   // do something
   Console.log(data)
})
### Preview
``` html
@realTime="realTime"
// Real time preview function
realTime (data) {
this.previews = data
}
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="option.img" :style="previews.img">
</div>
</div>
=
```
#### Image Move Callback Function @imgMoving
```
data type
{
moving: true, // moving ismove
axis: {
x1: 1, // Upper left corner
x2: 1// Upper right corner
y1: 1// Lower left corner
y2: 1 // Bottom right corner
}
}
```
#### Screenshot box move callback function @cropMoving
```
data type
{
moving: true, // moving 是否在移动
axis: {
x1: 1, // Upper left corner
x2: 1// Upper right corner
y1: 1// Lower left corner
y2: 1 // Bottom right corner
}
}
```
## Update log
### 0.54
` ` ` `
Remove log information
Fix PC Safari low version
` ` ` `
### 0.53
` ` ` `
Because chrome 81 kernel and IOS 13.5 fix the image rotation bug
The plug-in will not process rotation by default in the new version of browser, and it will be processed automatically in the lower version of browser
https://www.chromestatus.com/feature/6313474512650240
### 0.52
```
Recall the attribute of the minimum bullet box. There is a bug in the judgment of the dragging coordinate of the bullet box
```
## 0.51
`` `
Update the minimum attributes of the crop box, limit the minimum area, can pass more than 1 numbers and strings, limit the length and width are so large, can also pass arrays [90,90]
limitMinSize: [Number, Array, String]
`` `
### 0.50
Support picture empty
Fix ie11 ie10 not working
Fix the bug that URL.createObjectURL is not destroyed after creation
Add screenshot box to modify trigger event
this. $ emit ('change-crop-size', {
   width: this.cropW,
   height: this.cropH
})
### 0.49
Fix the default event of the scroll wheel
Fix the issue of event trigger in html static file
### 0.48
Fix display bug of mode attribute contain and cover
Fix the problem of cross domain display of base64 pictures under ios
### 0.47
Fix the problem that does not trigger preview for the first time
New image rendering mode function
### 0.46
Fix image rotation bug
Fix some bugs displayed
### 0.45
Add multiples using enlarge
You can output clipping boxes and other proportional images.
Thank you for your contribution from [https://github.com/hzsrc].
Add preview box to various proportions, and restore image screenshots decimal problem.
### 0.44
修复引入方式的问题
```
Repairing the way of introduction
import { VueCropper } from vue-cropper
components: {
VueCropper,
},
main.js
import VueCropper from vue-cropper
Vue.use(vueCropper)
cdn
<script src="vuecropper.js"></script>
Vue.use(window['vue-cropper'])
```
### V0.43
Peel off EXIF's dependency library, add exfi-min.js to reduce code size 45.9k = 37k
Build upgrade webpack4 upgrade
` ` ` ` ` ` ` ` ` '.
Add Vue install method = "npm: Vue.use (VueCropper) web: Vue.use (window['vue-cropper'])"
` ` ` ` ` ` ` ` ` '.
### V0.42
Repair screenshots because of the problem of removing decimal points.
### V0.41
Repair boundary problem of screenshots
### V0.40
The way to repair orientation
Thanks for the contribution of [Felipe Mengatto] (https://github.com/felipemengatto).
### v0.40
fix orientation handel
Thanks for the contribution of [Felipe Mengatto] (https://github.com/felipemengatto)
### v0.39
Fix problems caused by different orientation values
Thanks for the contribution of [Felipe Mengatto] (https://github.com/felipemengatto)
### v0.38
```
Modify coordinate feedback problem
```
### v0.37
```
Fix screenshot of centerBox out of 1px issue
Add screenshot Image move trigger event
```
### v0.36
```
Fix rotation automatically generates screenshot box error
Modify autocrop to dynamically generate screenshot boxes
```
### v0.35
```
Fix other images without compression issues
```
### v0.34
``` provides a solution for mobile crashes
Modify maxImgSize to 2000
```
### v0.33
``` provides a solution for mobile crashes
maxImgSize limits the maximum width and height of the image to 2000px by default.
```
### v0.32
```
Add screenshot box information display
infoTrue true to show the true output image width and height false show the width of the screenshot box
```
### v0.30
```
Added image coordinate function this.$refs.cropper.getImgAxis()
Added the capture box coordinate function this.$refs.cropper.getCropAxis()
Added compatibility with HD devices high
Added screenshot box to limit the function within the image centerbox
Added automatic generation of screenshot box function this.$refs.cropper.goAutoCrop
```
### v0.29
Added callback for image loading imgLoad returns result success, error
### v0.28
Fix the screenshot box fixed The screenshot box will affect the original image movement Zoom
### v0.27
Mouse scaling problem optimization
Img max-width style optimization
New attribute
CanMove can move pictures by default is yes
CanMoveBox move the screenshot box by default?
Original Whether to render in the original scale of the image Default is No
### v0.26
Fix Firefox browser mouse zoom problem
### v0.25
Fix image may not show
### v0.24
Fix ios photo rotation Screenshot problem Add auto fix image Screenshot preview code change, modify default upload image as blob preview
``` html
realTime (data) {
  this.previews = data
}
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
    'margin': '5px'}">
  <div :style="previews.div">
    <img :src="previews.url" :style="previews.img">
  </div>
</div>
```
### v0.23
Small optimization
### v0.22
 New modified image size function called by this.$refs.cropper.changeScale
### v0.21
Added fixed screenshot frame size fiexdBox (Note: It is best to use the automatic generation of screenshot box)
### v0.20
Added output original image scale screenshot props name full, fix zoom image too large sensitivity problem
### v0.19
Add image rotation to fix mac wheel over-sensitive
``` js
this.$refs.cropper.rotateRight() // Rotate 90 degrees to the right
this.$refs.cropper.rotateLeft() // Rotate 90 degrees to the left
```
### v0.18
Fix default build screenshot box over container error
### v0.17
Fix blob data acquisition error
### v0.15
Add mobile phone gesture zoom
```
canScale: true
```
### v0.13
Add preview
``` html
@realTime="realTime"
// Real time preview function
realTime (data) {
  this.previews = data
}
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
    'margin': '5px'}">
  <div :style="previews.div">
    <img :src="option.img" :style="previews.img">
  </div>
</div>
```
+6
View File
@@ -0,0 +1,6 @@
{
"presets": [
["es2015", { "modules": false }]
],
"comments": false
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

+122
View File
@@ -0,0 +1,122 @@
/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript */
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
background: none;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+24
View File
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
<meta http-equiv="Cache-Control" content="no-siteapp"/>
<meta content="yes" name="apple-mobile-web-app-capable" />
<meta content="black" name="apple-mobile-web-app-status-bar-style" />
<meta name="format-detection" content="telephone=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="full-screen" content="yes">
<meta name="x5-fullscreen" content="true">
<link rel="shortcut icon" href="./asset/favicon.ico">
<link rel="stylesheet" type="text/css" href="asset/prism.css">
<title>vue-cropper</title>
</head>
<body>
<div id="app"></div>
<script src="asset/prism.js"></script>
<script src="dist/build.js"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
{
"name": "vue-cropper-example",
"description": "A Vue.js project",
"author": "goodboy <463618622@qq.com>",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"exif-js": "^2.3.0",
"vue": "^2.5.17",
"vue-cropper": "^0.4.9",
"vue-loader": "^14.2.2",
"vue-template-compiler": "^2.5.17"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"babel-preset-es2015": "^6.13.2",
"cross-env": "^1.0.6",
"css-loader": "^0.23.1",
"file-loader": "^0.8.4",
"webpack": "^2.3.2",
"webpack-dev-server": "^3.1.11"
},
"browserify": {
"transform": [
"vueify"
]
}
}
+822
View File
@@ -0,0 +1,822 @@
<template>
<div class="wrapper">
<div class="model" v-show="model">
<div class="model-show" @click="model = false">
<img :src="modelSrc" alt="" @click="model = false">
</div>
</div>
<div class="content">
<h1><a class="title" href="https://github.com/xyxiao001/vue-cropper" target="_blank">vue-cropper</a></h1>
<iframe src="https://ghbtns.com/github-btn.html?user=xyxiao001&repo=vue-cropper&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
<div class="show-info">
<h2>install</h2>
<codes>
<div slot="body">{{ code0 }}</div>
</codes>
</div>
<div class="show-info">
<h2>example1 基本例子 无限制</h2>
<div class="test test1">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
:info="true"
:full="option.full"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:fixedBox="option.fixedBox"
:original="option.original"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:centerBox="option.centerBox"
:high="option.high"
:infoTrue="option.infoTrue"
:maxImgSize="option.maxImgSize"
@realTime="realTime"
@imgLoad="imgLoad"
@cropMoving="cropMoving"
:enlarge="option.enlarge"
:mode="option.mode"
:limitMinSize="option.limitMinSize"
></vueCropper>
</div>
<div class="test-button">
<button @click="changeImg" class="btn">changeImg</button>
<label class="btn" for="uploads">upload</label>
<input type="file" id="uploads" style="position:absolute; clip:rect(0 0 0 0);" accept="image/png, image/jpeg, image/gif, image/jpg" @change="uploadImg($event, 1)" ref="uploadImg">
<button @click="startCrop" v-if="!crap" class="btn">start</button>
<button @click="stopCrop" v-else class="btn">stop</button>
<button @click="clearCrop" class="btn">clear</button>
<button @click="refreshCrop" class="btn">refresh</button>
<button @click="changeScale(1)" class="btn">+</button>
<button @click="changeScale(-1)" class="btn">-</button>
<button @click="rotateLeft" class="btn">rotateLeft</button>
<button @click="rotateRight" class="btn">rotateRight</button>
<button @click="finish('base64')" class="btn">preview(base64)</button>
<button @click="finish('blob')" class="btn">preview(blob)</button>
<button @click="() => option.img = ''" class="btn">清除图片</button>
<a @click="down('base64')" class="btn">download(base64)</a>
<a @click="down('blob')" class="btn">download(blob)</a>
<a :href="downImg" download="demo.png" ref="downloadDom"></a>
</div>
<div class="pre">
<!-- <section class="pre-item">
<p>固定大小 固定宽度100px</p>
<section v-html="previews.html"></section>
</section> -->
<section class="pre-item">
<p>截图框大小</p>
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</section>
<section class="pre-item">
<p>中等大小</p>
<div :style="previewStyle1">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</section>
<section class="pre-item">
<p>迷你大小</p>
<div :style="previewStyle2">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</section>
<section class="pre-item" title="zoom: (100 / previews.w)">
<p>固定为100宽度</p>
<div :style="previewStyle3">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</section>
<section class="pre-item" title="zoom: (100 / previews.h)">
<p>固定为100高度</p>
<div :style="previewStyle4">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</section>
</div>
<div style="display:block; width: 100%;">
<label class="c-item">
<span>图片默认渲染方式</span>
<select v-model="option.mode">
<option value="contain">contain</option>
<option value="cover">cover</option>
<option value="400px auto">400px auto</option>
<option value="auto 400px">auto 400px</option>
<option value="50%">50%</option>
<option value="auto 50%">auto 50%</option>
</select>
<section>
类似css background属性设置 设置不符合规范不生效 参照文档说明
</section>
</label>
<!-- <label class="c-item">
<span>截图框最小限制 </span>
<input type="text" v-model="option.limitMinSize">
</label> -->
<label class="c-item">
<span>上传时图片最大大小(默认会压缩尺寸到这个大小)</span>
<input type="nubmer" v-model="option.maxImgSize">
</label>
<label class="c-item">
<span>上传图片是否显示原始宽高 (针对大图 可以铺满)</span>
<input type="checkbox" v-model="option.original">
<span>original: {{ option.original}}</span>
</label>
<label class="c-item">
<span>是否根据dpr生成适合屏幕的高清图片</span>
<input type="checkbox" v-model="option.high">
<span>high: {{ option.high}}</span>
</label>
<label class="c-item">
<span>是否输出原图比例的截图</span>
<input type="checkbox" v-model="option.full">
<span>full: {{ option.full}}</span>
</label>
<label class="c-item">
<span>截图信息展示是否是真实的输出宽高</span>
<input type="checkbox" v-model="option.infoTrue">
<span>infoTrue: {{ option.infoTrue}}</span>
</label>
<label class="c-item">
<span>能否拖动图片</span>
<input type="checkbox" v-model="option.canMove">
<span>canMove: {{ option.canMove}}</span>
</label>
<label class="c-item">
<span>能否拖动截图框</span>
<input type="checkbox" v-model="option.canMoveBox">
<span>canMoveBox: {{ option.canMoveBox}}</span>
</label>
<label class="c-item">
<span>截图框固定大小</span>
<input type="checkbox" v-model="option.fixedBox">
<span>fixedBox: {{ option.fixedBox}}</span>
</label>
<label class="c-item">
<span>是否自动生成截图框</span>
<input type="checkbox" v-model="option.autoCrop">
<span>autoCrop: {{ option.autoCrop}}</span>
</label>
<label class="c-item">
<span>自动生成截图框的宽高</span>
<span>宽度: </span><input type="number" v-model="option.autoCropWidth">
<span>高度: </span><input type="number" v-model="option.autoCropHeight">
</label>
<label class="c-item">
<span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
<input type="checkbox" v-model="option.centerBox">
<span>centerBox: {{ option.centerBox}}</span>
</label>
<label class="c-item">
<span>是否按照截图框比例输出 默认为1 </span>
<input type="number" v-model="option.enlarge">
</label>
<p>输出图片格式</p>
<label class="c-item">
<label>jpg <input type="radio" name="type" value="jpeg" v-model="option.outputType"></label>
<label>png <input type="radio" name="type" value="png" v-model="option.outputType"></label>
<label>webp <input type="radio" name="type" value="webp" v-model="option.outputType"></label>
</label>
</div>
<codes>
<div slot="body">{{ code1 }}</div>
</codes>
</div>
</div>
</div>
</template>
<script>
import VueCropper from "./vue-cropper/vue-cropper";
// import { VueCropper } from '../test'
import codes from "./code";
export default {
data: function() {
return {
model: false,
modelSrc: "",
crap: false,
previews: {},
lists: [
// {
// img: 'https://fengyuanchen.github.io/cropper/images/picture.jpg'
// },
{
img: "https://avatars2.githubusercontent.com/u/15681693?s=460&v=4"
},
{
img: "http://cdn.xyxiao.cn/Landscape_1.jpg"
},
{
img: "http://cdn.xyxiao.cn/Landscape_2.jpg"
},
{
img: "http://cdn.xyxiao.cn/Landscape_3.jpg"
},
{
img: "http://cdn.xyxiao.cn/Landscape_4.jpg"
},
{
img: "http://cdn.xyxiao.cn/Portrait_1.jpg"
},
{
img: "http://cdn.xyxiao.cn/Portrait_2.jpg"
}
],
option: {
img: "",
size: 1,
full: false,
outputType: "png",
canMove: true,
fixedBox: false,
original: false,
canMoveBox: true,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 200,
autoCropHeight: 150,
centerBox: false,
high: false,
cropData: {},
enlarge: 1,
mode: 'contain',
maxImgSize: 3000,
limitMinSize: [100, 120]
},
example2: {
img: "http://cdn.xyxiao.cn/Landscape_2.jpg",
info: true,
size: 1,
outputType: "jpeg",
canScale: true,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 300,
autoCropHeight: 250,
fixed: true,
// 真实的输出宽高
infoTrue: true,
fixedNumber: [4, 3]
},
example3: {
img: "http://cdn.xyxiao.cn/Landscape_1.jpg",
autoCrop: true,
autoCropWidth: 200,
autoCropHeight: 200,
fixedBox: true
},
downImg: "#",
previewStyle1: {},
previewStyle2: {},
previewStyle3: {},
previewStyle4: {},
code0: '',
code1: '',
code2: '',
code3: '',
preview3: '',
};
},
methods: {
changeImg() {
this.option.img = this.lists[~~(Math.random() * this.lists.length)].img;
},
startCrop() {
// start
this.crap = true;
this.$refs.cropper.startCrop();
},
stopCrop() {
// stop
this.crap = false;
this.$refs.cropper.stopCrop();
},
clearCrop() {
// clear
this.$refs.cropper.clearCrop();
},
refreshCrop() {
// clear
this.$refs.cropper.refresh();
},
changeScale(num) {
num = num || 1;
this.$refs.cropper.changeScale(num);
},
rotateLeft() {
this.$refs.cropper.rotateLeft();
},
rotateRight() {
this.$refs.cropper.rotateRight();
},
finish(type) {
// 输出
// var test = window.open('about:blank')
// test.document.body.innerHTML = '图片生成中..'
if (type === "blob") {
this.$refs.cropper.getCropBlob(data => {
var img = window.URL.createObjectURL(data);
this.model = true;
this.modelSrc = img;
});
} else {
this.$refs.cropper.getCropData(data => {
this.model = true;
this.modelSrc = data;
});
}
},
// 实时预览函数
realTime(data) {
var previews = data;
var h = 0.5;
var w = 0.2;
this.previewStyle1 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: h
};
this.previewStyle2 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: w
};
this.previewStyle3 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: (100 / previews.w)
};
this.previewStyle4 = {
width: previews.w + "px",
height: previews.h + "px",
overflow: "hidden",
margin: "0",
zoom: (100 / previews.h)
};
this.previews = data;
},
finish2(type) {
this.$refs.cropper2.getCropData(data => {
this.model = true;
this.modelSrc = data;
});
},
finish3(type) {
this.$refs.cropper3.getCropData(data => {
this.model = true;
this.modelSrc = data;
});
},
down(type) {
// event.preventDefault()
// 输出
if (type === "blob") {
this.$refs.cropper.getCropBlob(data => {
this.downImg = window.URL.createObjectURL(data);
if (window.navigator.msSaveBlob) {
var blobObject = new Blob([data]);
window.navigator.msSaveBlob(blobObject, "demo.png");
} else {
this.$nextTick(() => {
this.$refs.downloadDom.click();
});
}
});
} else {
this.$refs.cropper.getCropData(data => {
this.downImg = data;
if (window.navigator.msSaveBlob) {
var blobObject = new Blob([data]);
window.navigator.msSaveBlob(blobObject, "demo.png");
} else {
this.$nextTick(() => {
this.$refs.downloadDom.click();
});
}
});
}
},
uploadImg(e, num) {
//上传图片
// this.option.img
var file = e.target.files[0];
if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种");
return false;
}
var reader = new FileReader();
reader.onload = e => {
let data;
if (typeof e.target.result === "object") {
// 把Array Buffer转化为blob 如果是base64不需要
data = window.URL.createObjectURL(new Blob([e.target.result]));
} else {
data = e.target.result;
}
if (num === 1) {
this.option.img = data;
} else if (num === 2) {
this.example2.img = data;
}
this.$refs.uploadImg.value = ''
};
// 转化为base64
// reader.readAsDataURL(file)
// 转化为blob
reader.readAsArrayBuffer(file);
},
imgLoad(msg) {
console.log(msg);
},
cropMoving(data) {
this.option.cropData = data;
}
},
components: {
VueCropper,
codes
},
mounted() {
this.changeImg();
// hljs.configure({useBR: true})
var list = [].slice.call(document.querySelectorAll("pre code"));
list.forEach((val, index) => {
hljs.highlightBlock(val);
});
// console.log(this.$refs.cropper)
this.code0 = `
npm install vue-cropper
// 组件内使用
import { VueCropper } from 'vue-cropper'
components: {
VueCropper,
},
// main.js里面使用
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
// cdn方式使用
<script src="vuecropper.js">\<\/script>
Vue.use(window['vue-cropper'])
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
></vueCropper>
`
this.code1 = `
<div class="show-info">
<h2>example1 基本例子 无限制</h2>
<div class="test test1">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
:info="true"
:full="option.full"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:fixedBox="option.fixedBox"
:original="option.original"
:autoCrop="option.autoCrop"
:autoCropWidth="option.autoCropWidth"
:autoCropHeight="option.autoCropHeight"
:centerBox="option.centerBox"
:high="option.high"
:infoTrue="option.infoTrue"
@realTime="realTime"
@imgLoad="imgLoad"
@cropMoving="cropMoving"
:enlarge="option.enlarge"
></vueCropper>
</div>
<div class="test-button">
<button @click="changeImg" class="btn">changeImg</button>
<label class="btn" for="uploads">upload</label>
<input type="file" id="uploads" style="position:absolute; clip:rect(0 0 0 0);" accept="image/png, image/jpeg, image/gif, image/jpg" @change="uploadImg($event, 1)">
<button @click="startCrop" v-if="!crap" class="btn">start</button>
<button @click="stopCrop" v-else class="btn">stop</button>
<button @click="clearCrop" class="btn">clear</button>
<button @click="refreshCrop" class="btn">refresh</button>
<button @click="changeScale(1)" class="btn">+</button>
<button @click="changeScale(-1)" class="btn">-</button>
<button @click="rotateLeft" class="btn">rotateLeft</button>
<button @click="rotateRight" class="btn">rotateRight</button>
<button @click="finish('base64')" class="btn">preview(base64)</button>
<button @click="finish('blob')" class="btn">preview(blob)</button>
<a @click="down('base64')" class="btn">download(base64)</a>
<a @click="down('blob')" class="btn">download(blob)</a>
<a :href="downImg" download="demo.png" ref="downloadDom"></a>
</div>
<p>截图框大小</p>
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden',
'margin': '5px'}">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<p>中等大小</p>
<div :style="previewStyle1">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<p>迷你大小</p>
<div :style="previewStyle2">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
<div style="display:block; width: 100%;">
<label class="c-item">
<span>上传图片是否显示原始宽高 (针对大图 可以铺满)</span>
<input type="checkbox" v-model="option.original">
<span>original: {{ option.original}}</span>
</label>
<label class="c-item">
<span>是否根据dpr生成适合屏幕的高清图片</span>
<input type="checkbox" v-model="option.high">
<span>high: {{ option.high}}</span>
</label>
<label class="c-item">
<span>是否输出原图比例的截图</span>
<input type="checkbox" v-model="option.full">
<span>full: {{ option.full}}</span>
</label>
<label class="c-item">
<span>截图信息展示是否是真实的输出宽高</span>
<input type="checkbox" v-model="option.infoTrue">
<span>infoTrue: {{ option.infoTrue}}</span>
</label>
<label class="c-item">
<span>能否拖动图片</span>
<input type="checkbox" v-model="option.canMove">
<span>canMove: {{ option.canMove}}</span>
</label>
<label class="c-item">
<span>能否拖动截图框</span>
<input type="checkbox" v-model="option.canMoveBox">
<span>canMoveBox: {{ option.canMoveBox}}</span>
</label>
<label class="c-item">
<span>截图框固定大小</span>
<input type="checkbox" v-model="option.fixedBox">
<span>fixedBox: {{ option.fixedBox}}</span>
</label>
<label class="c-item">
<span>是否自动生成截图框</span>
<input type="checkbox" v-model="option.autoCrop">
<span>autoCrop: {{ option.autoCrop}}</span>
</label>
<label class="c-item">
<span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
<input type="checkbox" v-model="option.centerBox">
<span>centerBox: {{ option.centerBox}}</span>
</label>
<label class="c-item">
<span>是否按照截图框比例输出 默认为1 </span>
<input type="number" v-model="option.enlarge">
</label>
<p>输出图片格式</p>
<label class="c-item">
<label>jpg <input type="radio" name="type" value="jpeg" v-model="option.outputType"></label>
<label>png <input type="radio" name="type" value="png" v-model="option.outputType"></label>
<label>webp <input type="radio" name="type" value="webp" v-model="option.outputType"></label>
</label>
</div>
<codes>
<div slot="body">{{ code1 }}</div>
</codes>
</div>
`
}
};
</script>
<style>
* {
margin: 0;
padding: 0;
}
.content {
margin: auto;
max-width: 1200px;
margin-bottom: 100px;
}
.test-button {
display: flex;
flex-wrap: wrap;
}
.btn {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #c0ccda;
color: #1f2d3d;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 20px 10px 0px 0px;
padding: 9px 15px;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #50bfff;
border-color: #50bfff;
transition: all 0.2s ease;
text-decoration: none;
user-select: none;
}
.des {
line-height: 30px;
}
code.language-html {
padding: 10px 20px;
margin: 10px 0px;
display: block;
background-color: #333;
color: #fff;
overflow-x: auto;
font-family: Consolas, Monaco, Droid, Sans, Mono, Source, Code, Pro, Menlo,
Lucida, Sans, Type, Writer, Ubuntu, Mono;
border-radius: 5px;
white-space: pre;
}
.show-info {
margin-bottom: 50px;
}
.show-info h2 {
line-height: 50px;
}
/*.title, .title:hover, .title-focus, .title:visited {
color: black;
}*/
.title {
display: block;
text-decoration: none;
text-align: center;
line-height: 1.5;
margin: 20px 0px;
background-image: -webkit-linear-gradient(
left,
#3498db,
#f47920 10%,
#d71345 20%,
#f7acbc 30%,
#ffd400 40%,
#3498db 50%,
#f47920 60%,
#d71345 70%,
#f7acbc 80%,
#ffd400 90%,
#3498db
);
color: transparent;
-webkit-background-clip: text;
background-size: 200% 100%;
animation: slide 5s infinite linear;
font-size: 40px;
}
.test {
height: 500px;
}
.model {
position: fixed;
z-index: 10;
width: 100vw;
height: 100vh;
overflow: auto;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.8);
}
.model-show {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
text-align: center;
}
.model img {
display: block;
margin: auto;
max-width: 80%;
width: auto;
user-select: none;
background-position: 0px 0px, 10px 10px;
background-size: 20px 20px;
background-image: linear-gradient(
45deg,
#eee 25%,
transparent 25%,
transparent 75%,
#eee 75%,
#eee 100%
),
linear-gradient(45deg, #eee 25%, white 25%, white 75%, #eee 75%, #eee 100%);
}
.c-item {
display: block;
padding: 10px 0;
user-select: none;
}
.pre {
display: flex;
flex-wrap: wrap;
}
.pre-item {
padding-right: 20px;
}
@keyframes slide {
0% {
background-position: 0 0;
}
100% {
background-position: -100% 0;
}
}
@media screen and (max-width: 1000px) {
.content {
max-width: 90%;
margin: auto;
}
.test {
height: 400px;
}
}
</style>
+60
View File
@@ -0,0 +1,60 @@
<template>
<section :class="{'preview': !all, 'all': all}">
<code class="language-html">
<slot name="body"></slot>
</code>
<div class="show" @click="() => all = !all">{{ msg }}</div>
</section>
</template>
<script>
export default {
data: function () {
return {
all: false
}
},
computed: {
msg () {
return !this.all ? 'unfold' : 'fold'
}
}
}
</script>
<style lang="css" scoped>
section code.language-html {
padding: 10px 20px;
margin: 10px 0px;
display: block;
background-color: #333;
color: #fff;
overflow-x: auto;
font-family: Consolas, Monaco, Droid, Sans, Mono, Source, Code, Pro, Menlo, Lucida, Sans, Type, Writer, Ubuntu, Mono;
border-radius: 5px;
white-space: pre;
}
.preview {
position: relative;
height: 150px;
overflow: hidden;
}
.all{
position: relative;
}
.show {
position: absolute;
width: 100%;
text-align: center;
left: 0;
bottom: 0;
line-height: 50px;
height: 50px;
cursor: pointer;
background-color: rgba(255, 255, 255, 0.3);
color: white;
}
</style>
+7
View File
@@ -0,0 +1,7 @@
import Vue from 'vue'
import App from './App.vue'
import 'babel-polyfill'
new Vue({
el: '#app',
render: h => h(App)
})
@@ -0,0 +1,159 @@
const Exif = {};
Exif.getData = (img) => new Promise((reslove, reject) => {
let obj = {};
getImageData(img).then(data => {
obj.arrayBuffer = data;
obj.orientation = getOrientation(data);
reslove(obj)
}).catch(error => {
reject(error)
})
})
// 这里的获取exif要将图片转ArrayBuffer对象,这里假设获取了图片的baes64
// 步骤一
// base64转ArrayBuffer对象
function getImageData(img) {
let data = null;
return new Promise((reslove, reject) => {
if (img.src) {
if (/^data\:/i.test(img.src)) { // Data URI
data = base64ToArrayBuffer(img.src);
reslove(data)
} else if (/^blob\:/i.test(img.src)) { // Object URL
var fileReader = new FileReader();
fileReader.onload = function (e) {
data = e.target.result;
reslove(data)
};
objectURLToBlob(img.src, function (blob) {
fileReader.readAsArrayBuffer(blob);
});
} else {
var http = new XMLHttpRequest();
http.onload = function () {
if (this.status == 200 || this.status === 0) {
data = http.response
reslove(data)
} else {
throw "Could not load image";
}
http = null;
};
http.open("GET", img.src, true);
http.responseType = "arraybuffer";
http.send(null);
}
} else {
reject('img error')
}
})
}
function objectURLToBlob(url, callback) {
var http = new XMLHttpRequest();
http.open("GET", url, true);
http.responseType = "blob";
http.onload = function (e) {
if (this.status == 200 || this.status === 0) {
callback(this.response);
}
};
http.send();
}
function base64ToArrayBuffer(base64) {
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
var binary = atob(base64);
var len = binary.length;
var buffer = new ArrayBuffer(len);
var view = new Uint8Array(buffer);
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
return buffer;
}
// 步骤二,Unicode码转字符串
// ArrayBuffer对象 Unicode码转字符串
function getStringFromCharCode(dataView, start, length) {
var str = '';
var i;
for (i = start, length += start; i < length; i++) {
str += String.fromCharCode(dataView.getUint8(i));
}
return str;
}
// 步骤三,获取jpg图片的exif的角度(在ios体现最明显)
function getOrientation(arrayBuffer) {
var dataView = new DataView(arrayBuffer);
var length = dataView.byteLength;
var orientation;
var exifIDCode;
var tiffOffset;
var firstIFDOffset;
var littleEndian;
var endianness;
var app1Start;
var ifdStart;
var offset;
var i;
// Only handle JPEG image (start by 0xFFD8)
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
offset = 2;
while (offset < length) {
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
app1Start = offset;
break;
}
offset++;
}
}
if (app1Start) {
exifIDCode = app1Start + 4;
tiffOffset = app1Start + 10;
if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
endianness = dataView.getUint16(tiffOffset);
littleEndian = endianness === 0x4949;
if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
if (firstIFDOffset >= 0x00000008) {
ifdStart = tiffOffset + firstIFDOffset;
}
}
}
}
}
if (ifdStart) {
length = dataView.getUint16(ifdStart, littleEndian);
for (i = 0; i < length; i++) {
offset = ifdStart + i * 12 + 2;
if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
// 8 is the offset of the current tag's value
offset += 8;
// Get the original orientation value
orientation = dataView.getUint16(offset, littleEndian);
// Override the orientation with its default value for Safari (#120)
// if (IS_SAFARI_OR_UIWEBVIEW) {
// dataView.setUint16(offset, 1, littleEndian);
// }
break;
}
}
}
return orientation;
}
export default Exif
+19
View File
@@ -0,0 +1,19 @@
import VueCropper from './vue-cropper'
const install = function(Vue) {
Vue.component('VueCropper', VueCropper);
}
/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export { VueCropper }
export default {
version: '0.5.5',
install,
VueCropper,
vueCropper: VueCropper
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+60
View File
@@ -0,0 +1,60 @@
var path = require('path')
var webpack = require('webpack')
// var { VueLoaderPlugin } = require('vue-loader');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
resolve: {
extensions: ['.js', '.vue' ]
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
query: {
name: '[name].[ext]?[hash]'
}
}
]
},
devServer: {
historyApiFallback: true,
host: '0.0.0.0',
port: 8888,
disableHostCheck: true,
noInfo: true
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
])
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
{
"name": "vue-cropper",
"version": "0.5.5",
"description": "A simple Vue picture clipping plugin",
"keywords": [
"vue",
"cropper",
"vue-cropper",
"vue-component",
"vue-cropper-component"
],
"main": "./dist/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/xyxiao001/vue-cropper.git"
},
"author": "goodboy",
"license": "ISC",
"bugs": {
"url": "https://github.com/xyxiao001/vue-cropper/issues"
},
"homepage": "https://github.com/xyxiao001/vue-cropper#readme",
"scripts": {
"build": "rm -rf ./dist && webpack --config webpack.config.js"
},
"devDependencies": {
"vue-loader": "^15.4.2",
"webpack-cli": "^3.1.2",
"@babel/core": "^7.1.2",
"@babel/plugin-transform-runtime": "^7.1.0",
"@babel/preset-env": "^7.1.0",
"babel-loader": "^8.0.0-beta.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-runtime": "^6.26.0",
"css-loader": "^1.0.0",
"style-loader": "^0.23.1",
"vue": "^2.5.17",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.20.2",
"babel-polyfill": "^6.26.0"
},
"browserify": {
"transform": [
"vueify"
]
}
}
+159
View File
@@ -0,0 +1,159 @@
const Exif = {};
Exif.getData = (img) => new Promise((reslove, reject) => {
let obj = {};
getImageData(img).then(data => {
obj.arrayBuffer = data;
obj.orientation = getOrientation(data);
reslove(obj)
}).catch(error => {
reject(error)
})
})
// 这里的获取exif要将图片转ArrayBuffer对象,这里假设获取了图片的baes64
// 步骤一
// base64转ArrayBuffer对象
function getImageData(img) {
let data = null;
return new Promise((reslove, reject) => {
if (img.src) {
if (/^data\:/i.test(img.src)) { // Data URI
data = base64ToArrayBuffer(img.src);
reslove(data)
} else if (/^blob\:/i.test(img.src)) { // Object URL
var fileReader = new FileReader();
fileReader.onload = function (e) {
data = e.target.result;
reslove(data)
};
objectURLToBlob(img.src, function (blob) {
fileReader.readAsArrayBuffer(blob);
});
} else {
var http = new XMLHttpRequest();
http.onload = function () {
if (this.status == 200 || this.status === 0) {
data = http.response
reslove(data)
} else {
throw "Could not load image";
}
http = null;
};
http.open("GET", img.src, true);
http.responseType = "arraybuffer";
http.send(null);
}
} else {
reject('img error')
}
})
}
function objectURLToBlob(url, callback) {
var http = new XMLHttpRequest();
http.open("GET", url, true);
http.responseType = "blob";
http.onload = function (e) {
if (this.status == 200 || this.status === 0) {
callback(this.response);
}
};
http.send();
}
function base64ToArrayBuffer(base64) {
base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
var binary = atob(base64);
var len = binary.length;
var buffer = new ArrayBuffer(len);
var view = new Uint8Array(buffer);
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
return buffer;
}
// 步骤二,Unicode码转字符串
// ArrayBuffer对象 Unicode码转字符串
function getStringFromCharCode(dataView, start, length) {
var str = '';
var i;
for (i = start, length += start; i < length; i++) {
str += String.fromCharCode(dataView.getUint8(i));
}
return str;
}
// 步骤三,获取jpg图片的exif的角度(在ios体现最明显)
function getOrientation(arrayBuffer) {
var dataView = new DataView(arrayBuffer);
var length = dataView.byteLength;
var orientation;
var exifIDCode;
var tiffOffset;
var firstIFDOffset;
var littleEndian;
var endianness;
var app1Start;
var ifdStart;
var offset;
var i;
// Only handle JPEG image (start by 0xFFD8)
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
offset = 2;
while (offset < length) {
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
app1Start = offset;
break;
}
offset++;
}
}
if (app1Start) {
exifIDCode = app1Start + 4;
tiffOffset = app1Start + 10;
if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
endianness = dataView.getUint16(tiffOffset);
littleEndian = endianness === 0x4949;
if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
if (firstIFDOffset >= 0x00000008) {
ifdStart = tiffOffset + firstIFDOffset;
}
}
}
}
}
if (ifdStart) {
length = dataView.getUint16(ifdStart, littleEndian);
for (i = 0; i < length; i++) {
offset = ifdStart + i * 12 + 2;
if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
// 8 is the offset of the current tag's value
offset += 8;
// Get the original orientation value
orientation = dataView.getUint16(offset, littleEndian);
// Override the orientation with its default value for Safari (#120)
// if (IS_SAFARI_OR_UIWEBVIEW) {
// dataView.setUint16(offset, 1, littleEndian);
// }
break;
}
}
}
return orientation;
}
export default Exif
+19
View File
@@ -0,0 +1,19 @@
import VueCropper from './vue-cropper'
const install = function(Vue) {
Vue.component('VueCropper', VueCropper);
}
/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export { VueCropper }
export default {
version: '0.5.5',
install,
VueCropper,
vueCropper: VueCropper
}
File diff suppressed because it is too large Load Diff
+423
View File
@@ -0,0 +1,423 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>test vue-cropper</title>
<style>
* {
margin: 0;
padding: 0;
}
.cut {
width: 500px;
height: 500px;
margin: 30px auto;
}
.c-item {
max-width: 800px;
margin: 10px auto;
margin-top: 20px;
}
.content {
margin: auto;
max-width: 1200px;
margin-bottom: 100px;
}
.test-button {
display: flex;
flex-wrap: wrap;
align-content: center;
justify-content: center;
}
.btn {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #c0ccda;
color: #1f2d3d;
text-align: center;
box-sizing: border-box;
outline: none;
margin:20px 10px 0px 0px;
padding: 9px 15px;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #50bfff;
border-color: #50bfff;
transition: all .2s ease;
text-decoration: none;
user-select: none;
}
.des {
line-height: 30px;
}
code.language-html {
padding: 10px 20px;
margin: 10px 0px;
display: block;
background-color: #333;
color: #fff;
overflow-x: auto;
font-family: Consolas, Monaco, Droid, Sans, Mono, Source, Code, Pro, Menlo, Lucida, Sans, Type, Writer, Ubuntu, Mono;
border-radius: 5px;
white-space: pre;
}
.show-info {
margin-bottom: 50px;
}
.show-info h2 {
line-height: 50px;
}
/*.title, .title:hover, .title-focus, .title:visited {
color: black;
}*/
.title {
display: block;
text-decoration: none;
text-align: center;
line-height: 1.5;
margin: 20px 0px;
background-image: -webkit-linear-gradient(left,#3498db,#f47920 10%,#d71345 20%,#f7acbc 30%,#ffd400 40%,#3498db 50%,#f47920 60%,#d71345 70%,#f7acbc 80%,#ffd400 90%,#3498db);
color: transparent;
-webkit-background-clip: text;
background-size: 200% 100%;
animation: slide 5s infinite linear;
font-size: 40px;
}
.test {
height: 500px;
}
.model {
position: fixed;
z-index: 10;
width: 100vw;
height: 100vh;
overflow: auto;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.8);
}
.model-show {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
.model img {
display: block;
margin: auto;
max-width: 80%;
user-select: none;
background-position: 0px 0px, 10px 10px;
background-size: 20px 20px;
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee 100%),linear-gradient(45deg, #eee 25%, white 25%, white 75%, #eee 75%, #eee 100%);
}
.c-item {
display: block;
user-select: none;
}
@keyframes slide {
0% {
background-position: 0 0;
}
100% {
background-position: -100% 0;
}
}
</style>
</head>
<body>
<div id="app">
<div class="model" v-show="model" @click="model = false">
<div class="model-show">
<img :src="modelSrc" alt="">
</div>
</div>
<p>例子</p>
<div class="cut">
<vue-cropper ref="cropper" :img="option.img" :output-size="option.size" :output-type="option.outputType" :info="true" :full="option.full"
:can-move="option.canMove" :can-move-box="option.canMoveBox" :fixed-box="option.fixedBox" :original="option.original"
:auto-crop="option.autoCrop" :auto-crop-width="option.autoCropWidth" :auto-crop-height="option.autoCropHeight" :center-box="option.centerBox"
@real-time="realTime" :high="option.high" ></vue-cropper>
</div>
<div class="test-button">
<button @click="changeImg" class="btn">changeImg</button>
<label class="btn" for="uploads">upload</label>
<input type="file" id="uploads" style="position:absolute; clip:rect(0 0 0 0);" accept="image/png, image/jpeg, image/gif, image/jpg"
@change="uploadImg($event, 1)">
<button @click="startCrop" v-if="!crap" class="btn">start</button>
<button @click="stopCrop" v-else class="btn">stop</button>
<button @click="clearCrop" class="btn">clear</button>
<button @click="refreshCrop" class="btn">refresh</button>
<button @click="changeScale(1)" class="btn">+</button>
<button @click="changeScale(-1)" class="btn">-</button>
<button @click="rotateLeft" class="btn">rotateLeft</button>
<button @click="rotateRight" class="btn">rotateRight</button>
<button @click="finish('base64')" class="btn">preview(base64)</button>
<button @click="finish('blob')" class="btn">preview(blob)</button>
<a @click="down('base64')" class="btn">download(base64)</a>
<a @click="down('blob')" class="btn">download(blob)</a>
<div style="display:block; width: 100%;">
<label class="c-item">
<span>上传图片是否显示原始宽高 (针对大图 可以铺满)</span>
<input type="checkbox" v-model="option.original">
<span>original: {{ option.original}}</span>
</label>
<label class="c-item">
<span>能否拖动图片</span>
<input type="checkbox" v-model="option.canMove">
</label>
<label class="c-item">
<span>能否拖动截图框</span>
<input type="checkbox" v-model="option.canMoveBox">
<span>canMoveBox: {{ option.canMoveBox}}</span>
</label>
<label class="c-item">
<span>截图框固定大小</span>
<input type="checkbox" v-model="option.fixedBox">
<span>fixedBox: {{ option.fixedBox}}</span>
</label>
<label class="c-item">
<span>是否输出原图比例的截图</span>
<input type="checkbox" v-model="option.full">
<span>full: {{ option.full}}</span>
</label>
<label class="c-item">
<span>是否自动生成截图框</span>
<input type="checkbox" v-model="option.autoCrop">
<span>autoCrop: {{ option.autoCrop}}</span>
</label>
<label class="c-item">
<span>是否根据dpr生成适合屏幕的高清图片</span>
<input type="checkbox" v-model="option.high">
<span>high: {{ option.high}}</span>
</label>
<label class="c-item">
<span>截图框是否限制在图片里(只有在自动生成截图框时才能生效)</span>
<input type="checkbox" v-model="option.centerBox">
<span>centerBox: {{ option.centerBox}}</span>
</label>
<label class="c-item">
<p>输出图片格式</p>
<label>jpg
<input type="radio" name="type" value="jpeg" v-model="option.outputType">
</label>
<label>png
<input type="radio" name="type" value="png" v-model="option.outputType">
</label>
<label>webp
<input type="radio" name="type" value="webp" v-model="option.outputType">
</label>
</label>
</div>
</div>
<div class="show-preview" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden', 'margin': '5px'}">
<div :style="previews.div">
<img :src="previews.url" :style="previews.img">
</div>
</div>
</div>
<!-- <script src="https://vuejs.org/js/vue.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="../dist/index.js"></script>
<script>
Vue.use(window['vue-cropper'])
var a = new Vue({
el: '#app',
data: {
model: false,
modelSrc: '',
crap: false,
previews: {},
lists: [
{
img: 'http://ofyaji162.bkt.clouddn.com/touxiang.jpg'
},
{
img: 'https://o90cnn3g2.qnssl.com/0C3ABE8D05322EAC3120DDB11F9D1F72.png'
},
{
img: 'http://ofyaji162.bkt.clouddn.com/bg1.jpg',
},
{
img: 'http://ofyaji162.bkt.clouddn.com/bg2.jpg',
},
{
img: 'http://ofyaji162.bkt.clouddn.com/can.jpg'
},
{
img: 'http://ofyaji162.bkt.clouddn.com/nightcat.jpg'
}
],
option: {
img: 'https://avatars3.githubusercontent.com/u/15681693',
size: 1,
full: false,
outputType: 'png',
canMove: true,
fixedBox: false,
original: false,
canMoveBox: true,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 200,
autoCropHeight: 150,
centerBox: false,
high: true
},
show: true
},
methods: {
changeImg() {
this.option.img = this.lists[~~(Math.random() * this.lists.length)].img
},
startCrop() {
// start
this.crap = true
this.$refs.cropper.startCrop()
},
stopCrop() {
// stop
this.crap = false
this.$refs.cropper.stopCrop()
},
clearCrop() {
// clear
this.$refs.cropper.clearCrop()
},
refreshCrop() {
// clear
this.$refs.cropper.refresh()
},
changeScale(num) {
num = num || 1
this.$refs.cropper.changeScale(num)
},
rotateLeft() {
this.$refs.cropper.rotateLeft()
},
rotateRight() {
this.$refs.cropper.rotateRight()
},
finish(type) {
// 输出
// var test = window.open('about:blank')
// test.document.body.innerHTML = '图片生成中..'
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
var img = window.URL.createObjectURL(data)
this.model = true
this.modelSrc = img
})
} else {
this.$refs.cropper.getCropData((data) => {
this.model = true
this.modelSrc = data
})
}
},
// 实时预览函数
realTime(data) {
console.log(data)
this.previews = data
},
finish2(type) {
this.$refs.cropper2.getCropData((data) => {
this.model = true
this.modelSrc = data
})
},
finish3(type) {
this.$refs.cropper3.getCropData((data) => {
this.model = true
this.modelSrc = data
})
},
down(type) {
// event.preventDefault()
var aLink = document.createElement('a')
aLink.download = 'demo'
// 输出
if (type === 'blob') {
this.$refs.cropper.getCropBlob((data) => {
this.downImg = window.URL.createObjectURL(data)
aLink.href = window.URL.createObjectURL(data)
aLink.click()
})
} else {
this.$refs.cropper.getCropData((data) => {
this.downImg = data
aLink.href = data
aLink.click()
})
}
},
uploadImg(e, num) {
//上传图片
// this.option.img
var file = e.target.files[0]
if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
alert('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种')
return false
}
var reader = new FileReader()
reader.onload = (e) => {
let data
if (typeof e.target.result === 'object') {
// 把Array Buffer转化为blob 如果是base64不需要
data = window.URL.createObjectURL(new Blob([e.target.result]))
} else {
data = e.target.result
}
if (num === 1) {
this.option.img = data
} else if (num === 2) {
this.example2.img = data
}
}
// 转化为base64
// reader.readAsDataURL(file)
// 转化为blob
reader.readAsArrayBuffer(file)
},
imgLoad(msg) {
console.log(msg)
}
},
mounted() {
console.log(window['vue-cropper'])
}
})
</script>
</body>
</html>
+51
View File
@@ -0,0 +1,51 @@
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
devtool: 'cheap-module-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './dist/'),
filename: 'index.js',
library: 'vue-cropper',
libraryTarget: 'umd',
umdNamedDefine: true
},
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/transform-runtime']
}
}
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
mode: "production",
plugins: [
// 请确保引入这个插件!
new VueLoaderPlugin()
]
}