123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- <template>
- <view class="tm-avatarCrop fixed t-0 l-0 black" :style="{ width: `${width}px`, height: `${height}px` }">
- <tm-sticky model="bottom">
- <view class="fulled flex-center mb-32">
- <tm-button size="m" text @click="$emit('cancel')">取消</tm-button>
- <tm-button size="m" @click="selectedImage">选择图片</tm-button>
- <tm-button size="m" @click="saveImage">确定</tm-button>
- </view>
- </tm-sticky>
- <view class="flex-center"><canvas id="AvatarCrop" canvas-id="AvatarCrop" :style="{ width: `${area_width}px`, height: `${area_height}px` }"></canvas></view>
- <movable-area class="absolute t-0 l-0 zIndex-n1" :style="{ width: `${width}px`, height: `${height}px` }">
- <movable-view
- :out-of-bounds="false"
- @scale="movaScaleChange"
- @change="movaChange"
- :x="areview_x"
- :y="areview_y"
- direction="all"
- :scale="true"
- :style="{ width: `${scale_w}px`, height: `${scale_h}px` }"
- >
- <image v-show="image_src" @load="loadImage" :src="image_src" :style="{ width: `${scale_w}px`, height: `${scale_h}px` }"></image>
- </movable-view>
- </movable-area>
- <view :style="{ width: `${width}px`, height: `${height}px` }" class="absolute tm-avatarCrop-bodywk t-0 l-0 zIndex-n16">
- <view
- class="tm-avatarCrop-area relative"
- :class="[isArc ? 'rounded' : '']"
- :style="{
- width: `${area_width}px`,
- height: `${area_height}px`,
- top: `${posArray[0].y}px`,
- left: `${posArray[0].x}px`
- }"
- >
- <view class="flex-center text-size-s" :style="{ height: pos_size + 'px' }">宽:{{ Math.floor(area_width) }},高:{{ Math.floor(area_height) }}</view>
- <block v-for="(item, index) in 4" :key="index">
- <view
- v-if="(isRatio == true && index !== 3) || index == 3"
- :key="index"
- :style="{ width: `${pos_size}px`, height: `${pos_size}px` }"
- @touchstart.stop.prevent="m_start($event, index)"
- @touchmove.stop.prevent="m_move($event, index)"
- @touchend.stop.prevent="m_end($event, index)"
- @mousedown.stop.prevent="m_start($event, index)"
- @mousemove.stop.prevent="m_move($event, index)"
- @mouseup.stop.prevent="m_end($event, index)"
- @mouseleave="m_end($event, index)"
- class="tm-avatarCrop-pos absolute black opacity-5"
- :class="[
- 'tm-avatarCrop-pos-'+index,
- index == 0?'tm-avatarCrop-area-top-left':'',
- index == 1?'tm-avatarCrop-area-top-right': '',
- index == 2?'tm-avatarCrop-area-bottom-left': '',
- index == 3?'tm-avatarCrop-area-bottom-right': ''
- ]"
- :id="`${index}`"
- >
-
- <tm-icons style="line-height: 0;" color="white" v-if="index !== 3" :size="22" dense name="icon-expand-alt"></tm-icons>
- <tm-icons style="line-height: 0;" color="white" v-else :size="22" dense name="icon-arrows-alt"></tm-icons>
- </view>
- </block>
- </view>
- </view>
- </view>
- </template>
- <script>
- /**
- * 图片裁剪
- * @property {Number} area-width = [] 默认300,裁剪框的宽度
- * @property {Number} area-height = [] 默认300,裁剪框的高度
- * @property {Number} quality = [] 默认1,图片压缩质量0-1
- * @property {String} fileType = [jpg|png] 默认 png
- * @property {Boolean} is-ratio = [] 默认false,是否允许用户调整裁剪框的大小
- * @property {Boolean} is-arc = [] 默认false,是否圆形
- */
- import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
- import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
- import tmSticky from '@/tm-vuetify/components/tm-sticky/tm-sticky.vue';
- import tmImages from '@/tm-vuetify/components/tm-images/tm-images.vue';
- export default {
- name: 'tm-avatarCrop',
- props: {
- areaWidth: {
- type: Number,
- default: 300
- },
- areaHeight: {
- type: Number,
- default: 300
- },
- //是否允许用户调整距形区域的大小 。
- isRatio: {
- type: Boolean,
- default: false
- },
- //是否圆形。
- isArc: {
- type: Boolean,
- default: false
- },
- quality: {
- type: Number,
- default: 1
- },
- fileType:{
- type:String,
- default:'png'
- },
- confirm: {
- type: Function,
- default: function(data) {
- return function(data) {};
- }
- }
- },
- computed: {},
- components: {
- tmIcons,
- tmButton,
- tmSticky,
- tmImages
- },
- data() {
- return {
- width: 0,
- height: 0,
- canvanid: 'AvatarCrop',
- showCanva: false,
- area_width: 0,
- area_height: 0,
- prevent_left: 0,
- prevent_top: 0,
- old_x: 0,
- old_y: 0,
- posArray: [],
- endDrage: true,
- pos_size: 24,
- image_src: '',
- scale_w: 0, //图片缩放的宽,
- scale_h: 0, //图片缩放的高。
- scale: 1,
- real_w: 0,
- real_h: 0,
- scale_areview_x: 0,
- scale_areview_y: 0,
- areview_x: 0,
- areview_y: 0,
- areview_new_x: 0,
- areview_new_y: 0,
- isAddImage: false
- };
- },
- destroyed() {},
- created() {
- let sys = uni.getSystemInfoSync();
- this.width = sys.windowWidth;
- this.height = sys.screenHeight;
- this.area_width = uni.upx2px(this.areaWidth);
- this.area_height = uni.upx2px(this.areaHeight);
- let dr = [];
- for (let i = 0; i < 4; i++) {
- dr.push({
- x: 0,
- y: 0
- });
- }
- dr[0].x = (this.width - this.area_width) / 2;
- dr[0].y = (this.height - this.area_height) / 2;
- this.posArray = [...dr];
- },
- async mounted() {
- let t = this;
- await this.jishunTopData();
-
- },
- methods: {
- async jishunTopData() {
- let t =this;
- this.$nextTick(async function() {
- this.listData = [];
- uni.$tm.sleep(100).then(s=>{
- let psd = uni.createSelectorQuery().in(t);
- psd.select('.tm-avatarCrop-pos-0').boundingClientRect()
- .select('.tm-avatarCrop-pos-1').boundingClientRect()
- .select('.tm-avatarCrop-pos-2').boundingClientRect()
- .select('.tm-avatarCrop-pos-3').boundingClientRect()
- .exec(function(p){
- let list = p;
- let dr = [...t.posArray];
- for (let i = 0; i < list.length; i++) {
-
- if(list[i]){
- dr.splice(parseInt(list[i].id), 1, {
- x: list[i].left,
- y: list[i].top
- });
- }
- }
- t.posArray = [...dr];
- })
- })
- });
- },
- async loadImage(e) {
- this.isAddImage = true;
- this.posArray.splice(0, 1, {
- x: (this.width - this.area_width) / 2,
- y: (this.height - this.area_height) / 2
- });
-
- await this.jishunTopData();
- this.$nextTick(async function() {
- let img_w = e.detail.width;
- let img_h = e.detail.height;
- this.real_w = img_w;
- this.real_h = img_h;
- let ratio_w = img_w >= this.width ? this.width : img_w;
- let ratio_h = ratio_w / (img_w / img_h);
- this.scale_w = ratio_w;
- this.scale_h = ratio_h;
- //图片宽大于高度时,
- this.areview_y = (this.height - this.scale_h) / 2;
- this.areview_x = (this.width - this.scale_w) / 2;
- this.areview_new_x = this.areview_x;
- this.areview_new_y = this.areview_y;
- });
- },
- selectedImage() {
- let t = this;
- uni.chooseImage({
- count: 1,
- sizeType: ['compressed'],
- success: function(res) {
- t.image_src = res.tempFilePaths[0];
- }
- });
- },
- saveImage() {
- if (!this.image_src) {
- uni.$tm.toast('未选择图片');
- return;
- }
- let t = this;
- this.$nextTick(async function() {
- let scale_x = this.posArray[0].x - this.areview_new_x;
- let scale_y = this.posArray[0].y - this.areview_new_y;
- //计算真实的xy时,需要通过原有的缩放的大小通过比例来放大或者缩小到真实在源图片上的坐标。
- let real_x = (this.real_w / (this.scale_w * this.scale)) * scale_x;
- let real_y = (this.real_h / (this.scale_h * this.scale)) * scale_y;
- let real_w = (this.real_w / (this.scale_w * this.scale)) * this.area_width;
- let real_h = real_w;
- if (this.isRatio) {
- real_h = (this.real_h / (this.scale_h * this.scale)) * this.area_height;
- }
- const ctx = uni.createCanvasContext('AvatarCrop', this);
- if (!ctx) return;
- //如果把框移动到图片外,则不要截取。
- if (real_x < 0 || real_y < 0) {
- uni.$tm.toast('请把框移入图片中');
- return;
- }
- if (this.isArc) {
- ctx.beginPath();
- ctx.arc(this.area_width / 2, this.area_width / 2, this.area_width / 2, 0, 2 * Math.PI);
- ctx.clip();
- }
- ctx.drawImage(this.image_src, real_x, real_y, real_w, real_h, 0, 0, this.area_width, this.area_height);
- uni.showLoading({ title: '...' });
- function getimage() {
- let rtx = uni.getSystemInfoSync().pixelRatio;
- let a_w = t.area_width * rtx;
- let a_h = t.area_height * rtx;
- console.log(a_w,a_h);
- uni.canvasToTempFilePath(
- {
- x: 0,
- y: 0,
- width: a_w,
- height: a_h,
- canvasId: 'AvatarCrop',
- quality:t.quality,
- fileType:t.fileType,
- success: function(res) {
- // 在H5平台下,tempFilePath 为 base64
- uni.hideLoading();
- t.$nextTick(function() {
- t.$emit('confirm', { width:a_w, height: a_h, src: res.tempFilePath });
- t.confirm({ width: a_w, height: a_h, src: res.tempFilePath });
- });
- },
- fail: function(res) {
- uni.$tm.toast('请重试');
- }
- },
- t
- );
- }
- ctx.draw(true, function() {
- getimage();
- });
- });
- },
- movaChange(e) {
- //当添加新图片时,这里的执行要比添加时的慢。因此会覆盖前面设置的xy
- if (!this.isAddImage) {
- //移动后,真实的x,y已经得到,不需要再计算缩放的xy
- //(因为uniapp的bug缩放后返回 的xy始终是原有的xy而不是真实的)
- this.scale_areview_x = 0;
- this.scale_areview_y = 0;
- this.areview_new_x = e.detail.x;
- this.areview_new_y = e.detail.y;
- } else {
- this.isAddImage = false;
- }
- },
- movaScaleChange(e) {
- //通过缩放,计算出缩放后的x,y的和原有的x,y之间的差值得到真实的x,y。
- //(因为uniapp的bug缩放后返回 的xy始终是原有的xy而不是真实的)
- let scale_x = -(this.scale_w - this.scale_w * e.detail.scale) / 2;
- let scale_y = (this.scale_h - this.scale_h * e.detail.scale) / 2;
- this.areview_new_x = -scale_x;
- this.areview_new_y = this.posArray[0].y - Math.abs(scale_y);
- // 保存缩放的比例。
- this.scale = e.detail.scale;
- },
- m_start(event, index) {
- event.preventDefault();
- event.stopPropagation();
- const ctx = uni.createCanvasContext('AvatarCrop', this);
- if (ctx) {
- ctx.clearRect(0, 0, this.area_width, this.area_height);
- ctx.draw();
- }
- var touch;
- if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
- touch = event.changedTouches[0];
- } else {
- touch = {
- pageX:event.pageX,
- pageY:event.pageY
- }
- }
- // #ifdef APP-VUE
- if (index == 0 || index == 2) {
- this.old_x = touch.pageX;
- } else if (index == 1 || index == 3) {
- this.old_x = touch.pageX + this.area_width;
- }
- if (index == 0 || index == 1) {
- this.old_y = touch.pageY;
- } else if (index == 2 || index == 3) {
- this.old_y = touch.pageY+ this.area_height;
- }
- // #endif
- // #ifndef APP-VUE
- if (index == 0 || index == 2) {
- this.old_x = touch.pageX - event.currentTarget.offsetLeft - this.posArray[index].x;
- } else if (index == 1 || index == 3) {
- this.old_x = touch.pageX - event.currentTarget.offsetLeft - this.posArray[index].x + this.area_width - this.pos_size;
- }
- if (index == 0 || index == 1) {
- this.old_y = touch.pageY - event.currentTarget.offsetTop - this.posArray[index].y;
- } else if (index == 2 || index == 3) {
- this.old_y = touch.pageY - event.currentTarget.offsetTop - this.posArray[index].y + this.area_height - this.pos_size;
- }
- // #endif
-
-
- this.endDrage = false;
- },
- m_move(event, index) {
- if (this.endDrage) return;
- let t = this;
- event.preventDefault();
- event.stopPropagation();
- var touch;
- if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
- var touch = event.changedTouches[0];
- } else {
- touch = {
- pageX:event.pageX,
- pageY:event.pageY
- }
- }
- // #ifdef APP-VUE
- let ch = touch.pageY - this.pos_size/2;
- let chx = touch.pageX - this.pos_size/2;
- // #endif
- // #ifndef APP-VUE
- let ch = touch.pageY - t.old_y;
- let chx = touch.pageX - t.old_x;
- // #endif
-
-
- let pos_size = this.pos_size;
- let x_cha_len = chx - t.posArray[index].x;
- let y_cha_len = ch - t.posArray[index].y;
- t.posArray.splice(index, 1, {
- x: chx,
- y: ch
- });
-
- let w = 0;
- let h = 0;
-
- if (index == 0) {
-
-
- t.posArray.splice(1, 1, {
- x: t.posArray[1].x,
- y: ch
- });
- t.posArray.splice(2, 1, {
- x: chx,
- y: t.posArray[2].y
- });
- w = t.posArray[1].x + pos_size - t.posArray[0].x;
- h = t.posArray[2].y + pos_size - t.posArray[0].y;
-
-
- } else if (index == 1) {
- t.posArray.splice(0, 1, {
- x: t.posArray[0].x,
- y: ch
- });
- t.posArray.splice(3, 1, {
- x: chx,
- y: t.posArray[3].y
- });
- w = t.posArray[1].x + pos_size - t.posArray[0].x;
- h = t.posArray[2].y + pos_size - t.posArray[1].y;
- } else if (index == 2) {
- t.posArray.splice(0, 1, {
- x: chx,
- y: t.posArray[0].y
- });
- t.posArray.splice(3, 1, {
- x: t.posArray[3].x,
- y: ch
- });
- w = t.posArray[3].x + pos_size - t.posArray[2].x;
- h = t.posArray[2].y + pos_size - t.posArray[1].y;
- }
- if (index !== 3) {
- this.area_width = w < 30 ? 30 : w;
- this.area_height = h < 30 ? 30 : h;
- } else {
- let top_x = chx - this.area_width + pos_size;
- let top_y = ch - this.area_height + pos_size;
- t.posArray.splice(0, 1, {
- x: top_x,
- y: top_y
- });
- t.posArray.splice(1, 1, {
- x: top_x + this.area_width - pos_size,
- y: top_y
- });
- t.posArray.splice(2, 1, {
- x: top_x,
- y: top_y + this.area_height - pos_size
- });
- }
- },
- m_end(event, index) {
- if (this.disabled) return;
- let t = this;
- event.preventDefault();
- event.stopPropagation();
- this.endDrage = true;
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .tm-avatarCrop {
- .tm-avatarCrop-bodywk {
- pointer-events: none;
- }
- .tm-avatarCrop-area {
- background-color: rgba(0, 0, 0, 0.3);
- border: 1px dotted rgba(255, 255, 255, 0.7);
- pointer-events: none;
- .tm-avatarCrop-pos {
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: grey;
- pointer-events: auto;
- &.tm-avatarCrop-area-top-left {
- left: 0;
- top: 0;
- transform: rotate(90deg);
- }
- &.tm-avatarCrop-area-top-right {
- right: 0;
- top: 0;
- }
- &.tm-avatarCrop-area-bottom-right {
- right: 0;
- bottom: 0;
- }
- &.tm-avatarCrop-area-bottom-left {
- left: 0;
- bottom: 0;
- }
- }
- }
- }
- </style>
|