u-avatar-cropper.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <template>
  2. <view class="content">
  3. <view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }">
  4. <canvas class="cropper" :disable-scroll="true" @touchstart="touchStart" @touchmove="touchMove"
  5. @touchend="touchEnd"
  6. :style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }"
  7. canvas-id="cropper" id="cropper"></canvas>
  8. <canvas class="cropper" :disable-scroll="true" :style="{
  9. position: 'fixed',
  10. top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
  11. left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
  12. width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
  13. height: `${cropperOpt.height * cropperOpt.pixelRatio}`
  14. }" canvas-id="targetId" id="targetId"></canvas>
  15. </view>
  16. <view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px' }">
  17. <!-- #ifdef H5 -->
  18. <view class="upload" @tap="uploadTap">选择图片</view>
  19. <!-- #endif -->
  20. <!-- #ifndef H5 -->
  21. <view class="upload" @tap="uploadTap">重新选择</view>
  22. <!-- #endif -->
  23. <view class="getCropperImage" @tap="getCropperImage(false)">确定</view>
  24. </view>
  25. </view>
  26. </template>
  27. <script>
  28. import WeCropper from './weCropper.js';
  29. export default {
  30. props: {
  31. // 裁剪矩形框的样式,其中可包含的属性为lineWidth-边框宽度(单位rpx),color: 边框颜色,
  32. // mask-遮罩颜色,一般设置为一个rgba的透明度,如"rgba(0, 0, 0, 0.35)"
  33. boundStyle: {
  34. type: Object,
  35. default () {
  36. return {
  37. lineWidth: 4,
  38. borderColor: 'rgb(245, 245, 245)',
  39. mask: 'rgba(0, 0, 0, 0.35)'
  40. };
  41. }
  42. }
  43. // // 裁剪框宽度,单位rpx
  44. // rectWidth: {
  45. // type: [String, Number],
  46. // default: 400
  47. // },
  48. // // 裁剪框高度,单位rpx
  49. // rectHeight: {
  50. // type: [String, Number],
  51. // default: 400
  52. // },
  53. // // 输出图片宽度,单位rpx
  54. // destWidth: {
  55. // type: [String, Number],
  56. // default: 400
  57. // },
  58. // // 输出图片高度,单位rpx
  59. // destHeight: {
  60. // type: [String, Number],
  61. // default: 400
  62. // },
  63. // // 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可
  64. // fileType: {
  65. // type: String,
  66. // default: 'jpg',
  67. // },
  68. // // 生成的图片质量
  69. // // H5上无效,目前不考虑使用此参数
  70. // quality: {
  71. // type: [Number, String],
  72. // default: 1
  73. // }
  74. },
  75. data() {
  76. return {
  77. // 底部导航的高度
  78. bottomNavHeight: 50,
  79. originWidth: 200,
  80. width: 0,
  81. height: 0,
  82. cropperOpt: {
  83. id: 'cropper',
  84. targetId: 'targetCropper',
  85. pixelRatio: 1,
  86. width: 0,
  87. height: 0,
  88. scale: 2.5,
  89. zoom: 8,
  90. cut: {
  91. x: (this.width - this.originWidth) / 2,
  92. y: (this.height - this.originWidth) / 2,
  93. width: this.originWidth,
  94. height: this.originWidth
  95. },
  96. boundStyle: {
  97. // #ifndef APP-HARMONY
  98. lineWidth: uni.upx2px(this.boundStyle.lineWidth),
  99. // #endif
  100. // #ifdef APP-HARMONY
  101. lineWidth: this.boundStyle.lineWidth / 2,
  102. // #endif
  103. mask: this.boundStyle.mask,
  104. color: this.boundStyle.borderColor
  105. }
  106. },
  107. // 裁剪框和输出图片的尺寸,高度默认等于宽度
  108. // 输出图片宽度,单位px
  109. destWidth: 200,
  110. // 裁剪框宽度,单位px
  111. rectWidth: 200,
  112. // 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
  113. fileType: 'jpg',
  114. src: '', // 选择的图片路径,用于在点击确定时,判断是否选择了图片
  115. };
  116. },
  117. onLoad(option) {
  118. let rectInfo = uni.getSystemInfoSync();
  119. this.width = rectInfo.windowWidth;
  120. this.height = rectInfo.windowHeight - this.bottomNavHeight;
  121. this.cropperOpt.width = this.width;
  122. this.cropperOpt.height = this.height;
  123. this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
  124. if (option.destWidth) this.destWidth = option.destWidth;
  125. if (option.rectWidth) {
  126. let rectWidth = Number(option.rectWidth);
  127. this.cropperOpt.cut = {
  128. x: (this.width - rectWidth) / 2,
  129. y: (this.height - rectWidth) / 2,
  130. width: rectWidth,
  131. height: rectWidth
  132. };
  133. }
  134. this.rectWidth = option.rectWidth;
  135. if (option.fileType) this.fileType = option.fileType;
  136. // 初始化
  137. this.cropper = new WeCropper(this.cropperOpt)
  138. .on('ready', ctx => {
  139. // wecropper is ready for work!
  140. })
  141. .on('beforeImageLoad', ctx => {
  142. // before picture loaded, i can do something
  143. })
  144. .on('imageLoad', ctx => {
  145. // picture loaded
  146. })
  147. .on('beforeDraw', (ctx, instance) => {
  148. // before canvas draw,i can do something
  149. });
  150. // 设置导航栏样式,以免用户在page.json中没有设置为黑色背景
  151. uni.setNavigationBarColor({
  152. frontColor: '#ffffff',
  153. backgroundColor: '#000000'
  154. });
  155. uni.chooseImage({
  156. count: 1, // 默认9
  157. sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
  158. sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
  159. success: res => {
  160. this.src = res.tempFilePaths[0];
  161. // 获取裁剪图片资源后,给data添加src属性及其值
  162. this.cropper.pushOrign(this.src);
  163. }
  164. });
  165. },
  166. methods: {
  167. touchStart(e) {
  168. this.cropper.touchStart(e);
  169. },
  170. touchMove(e) {
  171. this.cropper.touchMove(e);
  172. },
  173. touchEnd(e) {
  174. this.cropper.touchEnd(e);
  175. },
  176. getCropperImage(isPre = false) {
  177. if (!this.src) return this.$u.toast('请先选择图片再裁剪');
  178. let cropper_opt = {
  179. destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值
  180. destWidth: Number(this.destWidth),
  181. fileType: this.fileType
  182. };
  183. this.cropper.getCropperImage(cropper_opt, (path, err) => {
  184. if (err) {
  185. uni.showModal({
  186. title: '温馨提示',
  187. content: err.message
  188. });
  189. } else {
  190. if (isPre) {
  191. uni.previewImage({
  192. current: '', // 当前显示图片的 http 链接
  193. urls: [path] // 需要预览的图片 http 链接列表
  194. });
  195. } else {
  196. uni.$emit('uAvatarCropper', path);
  197. this.$u.route({
  198. type: 'back'
  199. });
  200. }
  201. }
  202. });
  203. },
  204. uploadTap() {
  205. const self = this;
  206. uni.chooseImage({
  207. count: 1, // 默认9
  208. sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
  209. sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
  210. success: (res) => {
  211. self.src = res.tempFilePaths[0];
  212. // 获取裁剪图片资源后,给data添加src属性及其值
  213. self.cropper.pushOrign(this.src);
  214. }
  215. });
  216. }
  217. }
  218. };
  219. </script>
  220. <style scoped lang="scss">
  221. @import '../../libs/css/style.components.scss';
  222. .content {
  223. background: rgba(255, 255, 255, 1);
  224. }
  225. .cropper {
  226. position: absolute;
  227. top: 0;
  228. left: 0;
  229. width: 100%;
  230. height: 100%;
  231. z-index: 11;
  232. }
  233. .cropper-buttons {
  234. background-color: #000000;
  235. color: #eee;
  236. }
  237. .cropper-wrapper {
  238. position: relative;
  239. @include vue-flex;
  240. flex-direction: row;
  241. justify-content: space-between;
  242. align-items: center;
  243. width: 100%;
  244. background-color: #000;
  245. }
  246. .cropper-buttons {
  247. width: 100vw;
  248. @include vue-flex;
  249. flex-direction: row;
  250. justify-content: space-between;
  251. align-items: center;
  252. position: fixed;
  253. bottom: 0;
  254. left: 0;
  255. font-size: 28rpx;
  256. }
  257. .cropper-buttons .upload,
  258. .cropper-buttons .getCropperImage {
  259. width: 50%;
  260. text-align: center;
  261. }
  262. .cropper-buttons .upload {
  263. text-align: left;
  264. padding-left: 50rpx;
  265. }
  266. .cropper-buttons .getCropperImage {
  267. text-align: right;
  268. padding-right: 50rpx;
  269. }
  270. </style>