|
@@ -0,0 +1,538 @@
|
|
|
+const ABS = Math.abs
|
|
|
+const calcLen = (v) => { // distance between two coordinate
|
|
|
+ return Math.sqrt(v.x * v.x + v.y * v.y)
|
|
|
+}
|
|
|
+const calcAngle = (a, b) => { // angle of the two vectors
|
|
|
+ var l = calcLen(a) * calcLen(b); var cosValue; var angle
|
|
|
+ if (l) {
|
|
|
+ cosValue = (a.x * b.x + a.y * b.y) / l
|
|
|
+ angle = Math.acos(Math.min(cosValue, 1))
|
|
|
+ angle = a.x * b.y - b.x * a.y > 0 ? -angle : angle
|
|
|
+ return angle * 180 / Math.PI
|
|
|
+ }
|
|
|
+ return 0
|
|
|
+}
|
|
|
+const generateCanvasId = () => { // generate a random string
|
|
|
+ const seeds = 'abcdefghijklmnopqrstuvwxyz'
|
|
|
+ const arr = seeds.split('').concat(seeds.toUpperCase().split('')).concat('0123456789'.split(''))
|
|
|
+ let m = arr.length; let i
|
|
|
+ while (m) {
|
|
|
+ i = Math.floor(Math.random() * m--)
|
|
|
+ const temp = arr[m]
|
|
|
+ arr[m] = arr[i]
|
|
|
+ arr[i] = temp
|
|
|
+ }
|
|
|
+ return arr.slice(0, 16).join('')
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ props: {
|
|
|
+ width: { // width of the container
|
|
|
+ type: [String, Number],
|
|
|
+ default: '100%'
|
|
|
+ },
|
|
|
+ height: { // height of the container
|
|
|
+ type: [String, Number],
|
|
|
+ default: '100%'
|
|
|
+ },
|
|
|
+ cutWidth: { // cutter width
|
|
|
+ type: [String, Number],
|
|
|
+ default: '50%'
|
|
|
+ },
|
|
|
+ cutHeight: { // cutter height
|
|
|
+ type: [String, Number],
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ minWidth: { // minWidth of the cutter
|
|
|
+ type: Number,
|
|
|
+ default: 50
|
|
|
+ },
|
|
|
+ minHeight: { // minHeight of the cutter
|
|
|
+ type: Number,
|
|
|
+ default: 50
|
|
|
+ },
|
|
|
+ center: { // autoCenter
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ src: String,
|
|
|
+ disableScale: Boolean, // disable to zoom
|
|
|
+ disableRotate: Boolean,
|
|
|
+ disableTranslate: Boolean,
|
|
|
+ disableCtrl: Boolean, // disable to resize the cutter
|
|
|
+ boundDetect: Boolean, // open boundary detection
|
|
|
+ freeBoundDetect: Boolean, // open boundary detection while doing rotation
|
|
|
+ keepRatio: Boolean, // keep the ratio of the cutter
|
|
|
+ disablePreview: Boolean, // disable preview after cutting
|
|
|
+ showCtrlBorder: Boolean, // show cutter border
|
|
|
+ resetCut: Boolean, // reset cut while img change
|
|
|
+ fit: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ imageCenter: Boolean, // auto center/middle for image
|
|
|
+ maxZoom: { // maximum scaling factor
|
|
|
+ type: Number,
|
|
|
+ default: 10 // can not be Infinity in baidu-MiniProgram
|
|
|
+ },
|
|
|
+ minZoom: { // minimum scaling factor
|
|
|
+ type: Number,
|
|
|
+ default: 1
|
|
|
+ },
|
|
|
+ angle: { // initial angle of rotation
|
|
|
+ type: Number,
|
|
|
+ default: 0
|
|
|
+ },
|
|
|
+ zoom: { // initial scaling factor
|
|
|
+ type: Number,
|
|
|
+ default: 1
|
|
|
+ },
|
|
|
+ offset: { // initial offset relative to the cutter left border
|
|
|
+ type: Array,
|
|
|
+ default() {
|
|
|
+ return [0, 0]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ background: {
|
|
|
+ type: String,
|
|
|
+ default: '#000'
|
|
|
+ },
|
|
|
+ canvasBackground: { // background for the exported image
|
|
|
+ type: String,
|
|
|
+ default: '#fff'
|
|
|
+ },
|
|
|
+ canvasZoom: { // export multiples of the cutter size
|
|
|
+ type: Number,
|
|
|
+ default: 1
|
|
|
+ },
|
|
|
+ fileType: {
|
|
|
+ type: String,
|
|
|
+ default: 'png',
|
|
|
+ validator(t) {
|
|
|
+ return ['png', 'jpg'].includes(t)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ quality: {
|
|
|
+ type: Number,
|
|
|
+ default: 1
|
|
|
+ },
|
|
|
+ maskType: { // type for mask
|
|
|
+ type: String,
|
|
|
+ default: "shadow"
|
|
|
+ },
|
|
|
+ circleView: Boolean // circle clip view
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ transform: {
|
|
|
+ angle: 0,
|
|
|
+ translate: {
|
|
|
+ x: 0,
|
|
|
+ y: 0
|
|
|
+ },
|
|
|
+ zoom: 1
|
|
|
+ },
|
|
|
+ corner: {
|
|
|
+ left: 50,
|
|
|
+ right: 50,
|
|
|
+ bottom: 50,
|
|
|
+ top: 50
|
|
|
+ },
|
|
|
+ image: {
|
|
|
+ originWidth: 0,
|
|
|
+ originHeight: 0,
|
|
|
+ width: 0,
|
|
|
+ height: 0
|
|
|
+ },
|
|
|
+ ctrlWidth: 0,
|
|
|
+ ctrlHeight: 0,
|
|
|
+ view: false,
|
|
|
+ canvasId: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ transformMeta: function() {
|
|
|
+ const transform = this.transform
|
|
|
+ return `translate3d(${transform.translate.x}px, ${transform.translate.y}px, 0) rotate(${transform.angle}deg) scale(${transform.zoom})`
|
|
|
+ },
|
|
|
+ ctrlStyle: function() {
|
|
|
+ const corner = this.corner
|
|
|
+ let cssStr = `left: ${corner.left}px;top: ${corner.top}px;right: ${corner.right}px;bottom: ${corner.bottom }px;`
|
|
|
+ if(this.maskType !== 'outline') {
|
|
|
+ cssStr += `box-shadow: 0 0 0 50000rpx rgba(0,0,0, ${this.view ? 0.8 : 0.4})`
|
|
|
+ } else {
|
|
|
+ cssStr += `outline: rgba(0,0,0, ${this.view ? 0.8 : 0.4}) solid 5000px`
|
|
|
+ }
|
|
|
+ return cssStr
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ src: function() {
|
|
|
+ if(this.resetCut) this.resetCutReact()
|
|
|
+ this.initImage()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.canvasId = generateCanvasId()
|
|
|
+ uni.getSystemInfo().then(result => {
|
|
|
+ result = result[1] || {windowWidth: 375, windowHeight: 736}
|
|
|
+ this.ratio = result.windowWidth / 750
|
|
|
+ this.windowHeight = result.windowHeight
|
|
|
+ this.init()
|
|
|
+ this.initCanvas()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ toPx(str) {
|
|
|
+ if (str.indexOf('%') !== -1) {
|
|
|
+ return Math.floor(Number(str.replace('%', '')) / 100 * this.containerWidth)
|
|
|
+ }
|
|
|
+ if (str.indexOf('rpx') !== -1) {
|
|
|
+ return Math.floor(Number(str.replace('rpx', '')) * this.ratio)
|
|
|
+ }
|
|
|
+ return Math.floor(Number(str.replace('px', '')))
|
|
|
+ },
|
|
|
+ initCanvas() {
|
|
|
+ // #ifdef MP-ALIPAY
|
|
|
+ const context = uni.createSelectorQuery()
|
|
|
+ // #endif
|
|
|
+ // #ifndef MP-ALIPAY
|
|
|
+ const context = uni.createSelectorQuery().in(this)
|
|
|
+ // #endif
|
|
|
+
|
|
|
+ // get contianer size
|
|
|
+ context.select('.nice-cropper').boundingClientRect()
|
|
|
+ context.exec(res => {
|
|
|
+ this.containerWidth = res[0].width
|
|
|
+ this.containerHeight = res[0].height
|
|
|
+ this.initCut()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ resetCutReact() {// init size and position of the cutter
|
|
|
+ this.ctrlWidth = Math.min(this.toPx(this.cutWidth), this.containerWidth)
|
|
|
+ if (this.cutHeight) {
|
|
|
+ this.ctrlHeight = Math.min(this.toPx(this.cutHeight), this.containerHeight)
|
|
|
+ } else { // 默认为正方形
|
|
|
+ this.ctrlHeight = Math.min(this.ctrlWidth, this.containerHeight)
|
|
|
+ }
|
|
|
+ const cornerStartX = this.center ? Math.floor((this.containerWidth - this.ctrlWidth) / 2) : 0
|
|
|
+ const cornerStartY = this.center ? Math.floor((this.containerHeight - this.ctrlHeight) / 2) : 0
|
|
|
+ this.cutRatio = this.ctrlHeight / this.ctrlWidth
|
|
|
+ this.corner = {
|
|
|
+ left: cornerStartX,
|
|
|
+ right: this.containerWidth - this.ctrlWidth - cornerStartX,
|
|
|
+ top: cornerStartY,
|
|
|
+ bottom: this.containerHeight - this.ctrlHeight - cornerStartY
|
|
|
+ }
|
|
|
+ },
|
|
|
+ initCut() {
|
|
|
+ this.resetCutReact()
|
|
|
+ this.initImage()
|
|
|
+ },
|
|
|
+ async initImage() {
|
|
|
+
|
|
|
+ if (!this.src) return
|
|
|
+
|
|
|
+ const [err, res] = await uni.getImageInfo({
|
|
|
+ src: this.src
|
|
|
+ })
|
|
|
+
|
|
|
+ if(err) {
|
|
|
+ this.$emit("error", err)
|
|
|
+ } else {
|
|
|
+ this.$emit('load', res)
|
|
|
+ }
|
|
|
+console.log(res.path)
|
|
|
+ // init image size
|
|
|
+ this.image.originWidth = err ? this.ctrlWidth : res.width
|
|
|
+ this.image.originHeight = err ? this.ctrlHeight : res.height
|
|
|
+ this.image.width = this.fit ? this.ctrlWidth : this.image.originWidth
|
|
|
+ this.image.height = err ? this.ctrlHeight : res.height / res.width * this.image.width
|
|
|
+ this.img = res.path
|
|
|
+
|
|
|
+ const offset = [0, 0]
|
|
|
+ if(this.imageCenter) {
|
|
|
+ offset[0] = (this.ctrlWidth - this.image.width) / 2
|
|
|
+ offset[1] = (this.ctrlHeight - this.image.height) / 2
|
|
|
+ }
|
|
|
+ offset[0] += this.offset[0] || 0
|
|
|
+ offset[1] += this.offset[1] || 0
|
|
|
+
|
|
|
+ this.setTranslate(offset)
|
|
|
+ this.setZoom(this.zoom)
|
|
|
+ this.transform.angle = this.freeBoundDetect || !this.disableRotate ? this.angle : 0
|
|
|
+
|
|
|
+ this.setBoundary() // boundary detect
|
|
|
+ this.preview() // preview
|
|
|
+ this.draw()
|
|
|
+ },
|
|
|
+ init() {
|
|
|
+ this.pretouch = {}
|
|
|
+ this.handles = {}
|
|
|
+ this.preVector = {
|
|
|
+ x: 0,
|
|
|
+ y: 0
|
|
|
+ }
|
|
|
+ this.distance = 30
|
|
|
+ this.touch = {}
|
|
|
+ this.movetouch = {}
|
|
|
+ this.cutMode = false
|
|
|
+ this.params = {
|
|
|
+ zoom: 1,
|
|
|
+ deltaX: 0,
|
|
|
+ deltaY: 0,
|
|
|
+ diffX: 0,
|
|
|
+ diffY: 0,
|
|
|
+ angle: 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ start(e) {
|
|
|
+ if(!this.src) e.preventDefault()
|
|
|
+ const point = e.touches ? e.touches[0] : e
|
|
|
+ const touch = this.touch
|
|
|
+ const now = Date.now()
|
|
|
+ touch.startX = point.pageX
|
|
|
+ touch.startY = point.pageY
|
|
|
+ touch.startTime = now
|
|
|
+ this.doubleTap = false
|
|
|
+ this.view = false
|
|
|
+ clearTimeout(this.previewTimer)
|
|
|
+ if (e.touches.length > 1) {
|
|
|
+ var point2 = e.touches[1]
|
|
|
+ this.preVector = {
|
|
|
+ x: point2.pageX - this.touch.startX,
|
|
|
+ y: point2.pageY - this.touch.startY
|
|
|
+ }
|
|
|
+ this.startDistance = calcLen(this.preVector)
|
|
|
+ } else {
|
|
|
+ let pretouch = this.pretouch
|
|
|
+ this.doubleTap = this.pretouch.time && now - this.pretouch.time < 300 && ABS(touch.startX - pretouch.startX) < 30 && ABS(touch.startY - pretouch.startY) < 30 && ABS(touch.startTime - pretouch.time) < 300
|
|
|
+ pretouch = { // reserve the last touch
|
|
|
+ startX: this.touch.startX,
|
|
|
+ startY: this.touch.startY,
|
|
|
+ time: this.touch.startTime
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ move(e) {
|
|
|
+ if(!this.src) return
|
|
|
+ const point = e.touches ? e.touches[0] : e
|
|
|
+ if (e.touches.length > 1) { // multi touch
|
|
|
+ const point2 = e.touches[1]
|
|
|
+ const v = {
|
|
|
+ x: point2.pageX - point.pageX,
|
|
|
+ y: point2.pageY - point.pageY
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.preVector.x !== null) {
|
|
|
+ if (this.startDistance) { // zoom
|
|
|
+ const len = calcLen(v)
|
|
|
+ this.params.zoom = calcLen(v) / this.startDistance
|
|
|
+ this.startDistance = len
|
|
|
+ this.cutMode && !this.disableCtrl ? this.setCut() : !this.disableScale && this.pinch()
|
|
|
+ }
|
|
|
+ // rotate
|
|
|
+ this.params.angle = calcAngle(v, this.preVector)
|
|
|
+ this.cutMode && !this.disableCtrl ? this.setCut() : !this.disableRotate && this.rotate()
|
|
|
+ }
|
|
|
+ this.preVector.x = v.x
|
|
|
+ this.preVector.y = v.y
|
|
|
+ } else { // translate
|
|
|
+ const diffX = point.pageX - this.touch.startX
|
|
|
+ const diffY = point.pageY - this.touch.startY
|
|
|
+ this.params.diffY = diffY
|
|
|
+ this.params.diffX = diffX
|
|
|
+ if (this.movetouch.x) {
|
|
|
+ this.params.deltaX = point.pageX - this.movetouch.x
|
|
|
+ this.params.deltaY = point.pageY - this.movetouch.y
|
|
|
+ } else {
|
|
|
+ this.params.deltaX = this.params.deltaY = 0
|
|
|
+ }
|
|
|
+ if (ABS(diffX) > 30 || ABS(diffY) > 30) {
|
|
|
+ this.doubleTap = false
|
|
|
+ }
|
|
|
+ this.cutMode && !this.disableCtrl ? this.setCut() : !this.disableTranslate && this.translate()
|
|
|
+ this.movetouch.x = point.pageX
|
|
|
+ this.movetouch.y = point.pageY
|
|
|
+ }
|
|
|
+ !this.cutMode && this.setBoundary()
|
|
|
+ if (e.touches.length > 1) {
|
|
|
+ e.preventDefault()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ end() {
|
|
|
+ this.doubleTap && this.$emit('doubleTap')
|
|
|
+ this.cutMode && this.setBoundary()
|
|
|
+ this.init()
|
|
|
+ !this.disablePreview && this.preview()
|
|
|
+ this.draw()
|
|
|
+ },
|
|
|
+ translate() {
|
|
|
+ const transform = this.transform.translate
|
|
|
+ const meta = this.params
|
|
|
+ transform.x += meta.deltaX
|
|
|
+ transform.y += meta.deltaY
|
|
|
+ },
|
|
|
+ pinch() {
|
|
|
+ this.transform.zoom *= this.params.zoom
|
|
|
+ },
|
|
|
+ rotate() {
|
|
|
+ this.transform.angle += this.params.angle
|
|
|
+ },
|
|
|
+ setZoom(scale) {
|
|
|
+ scale = Math.min(Math.max(Number(scale) || 1, this.minZoom), this.maxZoom)
|
|
|
+ this.transform.zoom = scale
|
|
|
+ },
|
|
|
+ setTranslate(offset) {
|
|
|
+ if(Array.isArray(offset)) {
|
|
|
+ const x = Number(offset[0])
|
|
|
+ const y = Number(offset[1])
|
|
|
+ this.transform.translate.x = isNaN(x) ? this.transform.translate.x : this.corner.left + x
|
|
|
+ this.transform.translate.y = isNaN(y) ? this.transform.translate.y : this.corner.top + y
|
|
|
+ }
|
|
|
+ },
|
|
|
+ setRotate(angle) {
|
|
|
+ this.transform.angle = Number(angle) || 0
|
|
|
+ },
|
|
|
+ setTransform(x, y, angle, scale) {
|
|
|
+ this.setTranslate([x, y])
|
|
|
+ this.setZoom(scale)
|
|
|
+ this.setRotate(angle)
|
|
|
+ },
|
|
|
+ setCutMode(type) {
|
|
|
+ if(!this.src) return
|
|
|
+ this.cutMode = true
|
|
|
+ this.cutDirection = type
|
|
|
+ },
|
|
|
+ setCut() {
|
|
|
+ const corner = this.corner
|
|
|
+ const meta = this.params
|
|
|
+ this.setMeta(this.cutDirection, meta) // correct cutter position
|
|
|
+ if (this.keepRatio) {
|
|
|
+ if (this.cutDirection === 'lt' || this.cutDirection === 'rb') {
|
|
|
+ meta.deltaY = meta.deltaX * this.cutRatio
|
|
|
+ } else {
|
|
|
+ meta.deltaX = meta.deltaY / this.cutRatio
|
|
|
+ }
|
|
|
+ }
|
|
|
+ switch (this.cutDirection) {
|
|
|
+ case 'lt':
|
|
|
+ corner.top += meta.deltaY
|
|
|
+ corner.left += meta.deltaX
|
|
|
+ break
|
|
|
+ case 'rt':
|
|
|
+ corner.top += meta.deltaY
|
|
|
+ corner.right -= this.keepRatio ? -meta.deltaX : meta.deltaX
|
|
|
+ break
|
|
|
+ case 'rb':
|
|
|
+ corner.right -= meta.deltaX
|
|
|
+ corner.bottom -= meta.deltaY
|
|
|
+ break
|
|
|
+ case 'lb':
|
|
|
+ corner.bottom -= meta.deltaY
|
|
|
+ corner.left += this.keepRatio ? -meta.deltaX : meta.deltaX
|
|
|
+ break
|
|
|
+ }
|
|
|
+ this.ctrlWidth = this.containerWidth - corner.left - corner.right
|
|
|
+ this.ctrlHeight = this.containerHeight - corner.top - corner.bottom
|
|
|
+ },
|
|
|
+ setMeta(direction, meta) {
|
|
|
+ const {ctrlWidth, ctrlHeight, minWidth, minHeight } = this
|
|
|
+ switch(direction) {
|
|
|
+ case 'lt':
|
|
|
+ if(meta.deltaX > 0 || meta.deltaY > 0) {
|
|
|
+ meta.deltaX = Math.min(meta.deltaX, ctrlWidth - minWidth)
|
|
|
+ meta.deltaY = Math.min(meta.deltaY, ctrlHeight - minHeight)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 'rt':
|
|
|
+ if(meta.deltaX < 0 || meta.deltaY > 0) {
|
|
|
+ meta.deltaX = Math.max(meta.deltaX, minWidth - ctrlWidth)
|
|
|
+ meta.deltaY = Math.min(meta.deltaY, ctrlHeight - minHeight)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 'rb':
|
|
|
+ if(meta.deltaX < 0 || meta.deltaY < 0) {
|
|
|
+ meta.deltaX = Math.max(meta.deltaX, minWidth - ctrlWidth)
|
|
|
+ meta.deltaY = Math.max(meta.deltaY, minHeight - ctrlHeight)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 'lb':
|
|
|
+ if(meta.deltaX > 0 || meta.deltaY < 0) {
|
|
|
+ meta.deltaX = Math.min(meta.deltaX, ctrlWidth - minWidth)
|
|
|
+ meta.deltaY = Math.max(meta.deltaY, minHeight - ctrlHeight)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ setBoundary() {
|
|
|
+ let zoom = this.transform.zoom
|
|
|
+ zoom = zoom < this.minZoom ? this.minZoom : (zoom > this.maxZoom ? this.maxZoom : zoom)
|
|
|
+ this.transform.zoom = zoom
|
|
|
+ if (!this.boundDetect || !this.disableRotate && !this.freeBoundDetect) return true
|
|
|
+ const translate = this.transform.translate
|
|
|
+ const corner = this.corner
|
|
|
+ const minX = corner.left - this.image.width + this.ctrlWidth - this.image.width * (zoom - 1) / 2
|
|
|
+ const maxX = corner.left + this.image.width * (zoom - 1) / 2
|
|
|
+ const minY = corner.top - this.image.height + this.ctrlHeight - this.image.height * (zoom - 1) / 2
|
|
|
+ const maxY = corner.top + this.image.height * (zoom - 1) / 2
|
|
|
+ translate.x = Math.floor(translate.x < minX ? minX : (translate.x > maxX ? maxX : translate.x))
|
|
|
+ translate.y = Math.floor(translate.y < minY ? minY : (translate.y > maxY ? maxY : translate.y))
|
|
|
+ },
|
|
|
+ preview() {
|
|
|
+ clearTimeout(this.previewTimer)
|
|
|
+ this.previewTimer = setTimeout(() => {
|
|
|
+ this.view = true
|
|
|
+ }, 500)
|
|
|
+ },
|
|
|
+ draw() {
|
|
|
+ // #ifdef MP-ALIPAY
|
|
|
+ const context = uni.createCanvasContext(this.canvasId)
|
|
|
+ // #endif
|
|
|
+ // #ifndef MP-ALIPAY
|
|
|
+ const context = uni.createCanvasContext(this.canvasId, this)
|
|
|
+ // #endif
|
|
|
+ const transform = this.transform
|
|
|
+ const corner = this.corner
|
|
|
+ const canvasZoom = this.canvasZoom
|
|
|
+ const img = this.image
|
|
|
+ context.save()
|
|
|
+ context.setFillStyle(this.canvasBackground)
|
|
|
+ this.$emit('beforeDraw', context, transform) // beforeDraw hook
|
|
|
+
|
|
|
+ const zoom = transform.zoom
|
|
|
+ context.fillRect(0, 0, this.ctrlWidth * canvasZoom, this.ctrlHeight * canvasZoom) // clear canvas
|
|
|
+ context.translate((transform.translate.x - corner.left + img.width / 2) *canvasZoom, (transform.translate.y - corner.top + img.height / 2) * canvasZoom) // translate the canvas's orgin to the image center
|
|
|
+ context.rotate(transform.angle * Math.PI / 180)
|
|
|
+ context.translate(-img.width * zoom * 0.5 * canvasZoom, -img.height * zoom * 0.5 * canvasZoom)
|
|
|
+ context.drawImage(this.img, 0, 0, img.width * zoom * canvasZoom, img.height * zoom * canvasZoom)
|
|
|
+ context.restore()
|
|
|
+ this.$emit('afterDraw', context, {
|
|
|
+ width: this.ctrlWidth * canvasZoom,
|
|
|
+ height: this.ctrlHeight * canvasZoom
|
|
|
+ }) // afterDraw hook
|
|
|
+ context.draw(false, () => {
|
|
|
+ uni.canvasToTempFilePath({
|
|
|
+ canvasId: this.canvasId,
|
|
|
+ quality: this.quality || 1,
|
|
|
+ fileType: this.fileType,
|
|
|
+ success: (res) => {
|
|
|
+ this.$emit('cropped', res.tempFilePath, {
|
|
|
+ originWidth: this.image.originWidth,
|
|
|
+ originHeight: this.image.originHeight,
|
|
|
+ width: this.ctrlWidth * canvasZoom,
|
|
|
+ height: this.ctrlHeight * canvasZoom,
|
|
|
+ scale: zoom,
|
|
|
+ translate: {
|
|
|
+ x: transform.translate.x,
|
|
|
+ y: transform.translate.y
|
|
|
+ },
|
|
|
+ rotate: transform.angle
|
|
|
+ }) // draw callback
|
|
|
+ }
|
|
|
+ }, this)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|