当前位置:   article > 正文

小程序之图片裁剪(圆形、矩形)_微信小程序 把图片裁剪成不同形状

微信小程序 把图片裁剪成不同形状

这个裁剪方法可以裁剪圆形、矩形,可以二开,放心食用

先看效果图:

矩形的:

圆形的:

具体的方法:

组件的:

  1. // cropper--index.vue
  2. <template>
  3. <view>
  4. <canvas class="fyj_canvas" canvas-id="myCanvas" :style="{width:'100%', height:canvasHeight+'px',}"/>
  5. <movable-area class="fyj_movable_area text-center hidden" :style="{width:'100%', height:canvasHeight+'px',}">
  6. <movable-view v-if="src" :style="{width:cutWidth +'px', height:cutHeight + 'px'}" class="fyj_movable_view" :x="x"
  7. :y="y" direction="all" :scale="true" @change="movableChange" @scale="handleScale"></movable-view>
  8. <image class="fyj_photo" id="fyj_photo" :src="src" mode="widthFix"/>
  9. </movable-area>
  10. <!--</canvas>-->
  11. <view style="margin-top:20rpx; padding:0 20rpx;">
  12. <button class="pull-left" type="warn" size="mini" @click="getPhoto">选择照片/拍照</button>
  13. <button class="pull-right" type="primary" size="mini" @click="cut">裁剪</button>
  14. <view class="clearfix"></view>
  15. </view>
  16. </view>
  17. </template>
  18. <script setup>
  19. import {ref, reactive, watch, nextTick, getCurrentInstance} from 'vue'
  20. const {aspectRatio} = defineProps({
  21. // 这里定义了innerText属性,属性值可以在组件使用时指定
  22. //宽高比 TODO 这个要求裁剪框是矩形的,如果裁剪框是圆形的,这个属性宽高比一定得是1
  23. aspectRatio: {
  24. type: Number,
  25. default: 5 / 7,
  26. },
  27. })
  28. const screenWidth = ref(uni.getSystemInfoSync().windowWidth)
  29. const canvasHeight = ref(300)
  30. const x = ref(0)
  31. const y = ref(0)
  32. const src = ref('')
  33. const cut_src = ref('')
  34. const cutWidth = ref(0)
  35. const cutHeight = ref(0)
  36. const tempImage = ref('')
  37. const {proxy} = getCurrentInstance()
  38. const emits = defineEmits(['getTempFilePath'])
  39. // 这里是一个自定义方法
  40. //选择照片
  41. const getPhoto = () => {
  42. const ctx = uni.createCanvasContext('myCanvas', proxy)
  43. let obj = uni.createSelectorQuery();
  44. uni.chooseImage({
  45. count: 1,
  46. sizeType: ['original', 'compressed'],
  47. sourceType: ['album', 'camera'],
  48. success(res) {
  49. //清空之前的剪切图
  50. emits('getTempFilePath', {cut_src: '', cutWidth: cutWidth.value, cutHeight: cutHeight.value})
  51. // tempFilePath可以作为img标签的src属性显示图片
  52. const tempFilePaths = res.tempFilePaths[0];
  53. src.value = tempFilePaths
  54. // 这个变量是为了下面圆形裁剪框使用的 可以结合一个配置项判断是否要使用
  55. tempImage.value = tempFilePaths
  56. cut_src.value = ''
  57. setTimeout(function () {
  58. // TODO 这里也可以换成小程序获取图片信息的那个api方法
  59. uni.createSelectorQuery().in(proxy).select('#fyj_photo').boundingClientRect(function (rect) {
  60. console.log(rect);
  61. console.log('图片的信息', rect.height);
  62. canvasHeight.value = rect.height
  63. setCut();
  64. // TODO 矩形裁剪框使用的是这个
  65. // ctx.drawImage(tempFilePaths, 0, 0, screenWidth.value, canvasHeight.value)
  66. // ctx.draw()
  67. // 放到上面去了,防止一开始拿不到
  68. // setCut();
  69. //确保不同大小的图片,切图不会变形
  70. x.value = 0
  71. y.value = 0
  72. }).exec()
  73. }, 100)
  74. }
  75. })
  76. }
  77. //获取图片高度 暂时没用到
  78. // const getHeight = () => {
  79. // const query = uni.createSelectorQuery().in(proxy)
  80. // query.selectAll('#fyj_photo').boundingClientRect()
  81. // query.exec(function (rect) {
  82. // console.log(rect);
  83. // console.log(rect[0].height);
  84. // canvasHeight.value = rect[0].height
  85. // // TODO ...
  86. // ctx.drawImage(tempFilePaths[0], 0, 0, screenWidth.value, canvasHeight.value)
  87. // ctx.draw();
  88. // setCut();
  89. // })
  90. // }
  91. //裁剪框移动事件
  92. const movableChange = debounce((e) => {
  93. console.log('裁剪框移动', e);
  94. x.value = e.detail.x
  95. y.value = e.detail.y
  96. // TODO 这部分可以抽成一个方法配合一个配置项 圆形裁剪框
  97. const ctx = uni.createCanvasContext('myCanvas', proxy)
  98. setTimeout(function () {
  99. // TODO 这里也可以换成小程序获取图片信息的那个api方法
  100. uni.createSelectorQuery().in(proxy).select('#fyj_photo').boundingClientRect(function (rect) {
  101. console.log(rect);
  102. console.log('图片的信息', rect.height);
  103. canvasHeight.value = rect.height
  104. setCut();
  105. // TODO 圆形裁剪框 裁剪圆形的图片
  106. // 开始一个新的路径
  107. ctx.save()
  108. ctx.beginPath();
  109. // 创建一个圆形路径
  110. ctx.arc((x.value + (cutWidth.value / 2)), (y.value + (cutWidth.value / 2)), cutWidth.value / 2, 0, Math.PI * 2, false);
  111. // 设置当前路径为剪切路径
  112. ctx.clip();
  113. // 再次绘制图片,只有在剪切路径内的部分会被绘制
  114. ctx.drawImage(tempImage.value, 0, 0, screenWidth.value, canvasHeight.value);
  115. ctx.restore();
  116. ctx.draw();
  117. // 这个放到上面去了,防止一开始拿不到
  118. // setCut();
  119. // TODO 圆形裁剪框时是不需要的 确保不同大小的图片,切图不会变形
  120. // x.value = 0
  121. // y.value = 0
  122. // TODO 防止第二次圆形绘制失效
  123. ctx.clearRect(0, 0, screenWidth.value, canvasHeight.value)
  124. }).exec()
  125. }, 100)
  126. }, 500)
  127. //截图
  128. const cut = () => {
  129. console.log(cutHeight.value);
  130. uni.canvasToTempFilePath({
  131. // TODO 不管是绘制圆形还是矩形,这里的配置都是一样的
  132. x: x.value,
  133. y: y.value,
  134. width: cutWidth.value,
  135. height: cutHeight.value,
  136. destWidth: cutWidth.value,
  137. destHeight: cutHeight.value,
  138. canvasId: 'myCanvas',
  139. success(res) {
  140. console.log(res.tempFilePath);
  141. cut_src.value = res.tempFilePath
  142. emits('getTempFilePath', {cut_src: cut_src.value, cutWidth: cutWidth.value, cutHeight: cutHeight.value})
  143. }
  144. }, proxy)
  145. }
  146. //动态设置裁剪框大小,确定高度不得超过canvas的高度
  147. const setCut = () => {
  148. // TODO 这里比较重要
  149. cutWidth.value = uni.getSystemInfoSync().windowWidth * 0.8
  150. cutHeight.value = uni.getSystemInfoSync().windowWidth * 0.8 / aspectRatio
  151. if (cutHeight.value - 4 > canvasHeight.value) {
  152. console.log(cutHeight.value);
  153. console.log(canvasHeight.value);
  154. cutHeight.value = canvasHeight.value - 4
  155. cutWidth.value = (canvasHeight.value - 4) * aspectRatio
  156. } else {
  157. cutWidth.value = uni.getSystemInfoSync().windowWidth * 0.8
  158. cutHeight.value = uni.getSystemInfoSync().windowWidth * 0.8 / aspectRatio
  159. }
  160. console.log('裁剪框的宽', cutWidth.value);
  161. console.log('裁剪框的高', cutHeight.value);
  162. }
  163. // 裁剪框缩放事件
  164. const handleScale = debounce((e) => {
  165. console.log('裁剪框缩放', e);
  166. cutWidth.value = cutWidth.value * e.detail.scale
  167. cutHeight.value = cutWidth.value * e.detail.scale
  168. }, 500)
  169. // 防抖
  170. function debounce(func, wait, immediate=false) {
  171. let timeout;
  172. return function executedFunction() {
  173. const context = this;
  174. const args = arguments;
  175. const later = function() {
  176. timeout = null;
  177. if (!immediate) func.apply(context, args);
  178. };
  179. const callNow = immediate && !timeout;
  180. clearTimeout(timeout);
  181. timeout = setTimeout(later, wait);
  182. if (callNow) func.apply(context, args);
  183. };
  184. }
  185. </script>
  186. <style scoped lang="scss">
  187. // TODO 隐藏画布可以吧注释放开
  188. .fyj_canvas {
  189. //position: absolute;
  190. //left: 0;
  191. //top: -71vh;
  192. //z-index: -1;
  193. //opacity: 0;
  194. }
  195. .fyj_movable_area {
  196. width: 100%;
  197. height: auto;
  198. position: relative;
  199. background: rgba(0, 0, 0, 0.3);
  200. z-index: 99;
  201. }
  202. .fyj_movable_view {
  203. border: 2px dashed red;
  204. // TODO 圆形裁剪框时使用的
  205. border-radius: 50%;
  206. }
  207. .fyj_photo {
  208. width: 100%;
  209. }
  210. .fyj_footer {
  211. margin-top: 20rpx 0;
  212. }
  213. .fyj_footerBtn {
  214. width: 100%;
  215. display: inline-block;
  216. color: #fff;
  217. border-radius: 0;
  218. font-size: 32rpx;
  219. }
  220. .fyj_sure {
  221. background: #fc6b47;
  222. }
  223. .pull-left {
  224. float: left;
  225. }
  226. .pull-right {
  227. float: right;
  228. }
  229. .clearfix {
  230. clear: both;
  231. }
  232. .text-center {
  233. text-align: center;
  234. }
  235. </style>

 

使用组件:

  1. // testCropper--index.vue
  2. <template>
  3. <view>
  4. <!-- aspectRatio 剪裁图片的宽高比 -->
  5. <cropper :aspectRatio="1" @getTempFilePath="getCutsrc"></cropper>
  6. <view v-if="cut_src" class="fyj_cutDiv text-center">
  7. <image :style="{width: cutWidth + 'px', height: cutHeight + 'px'}" class="fyj_cut_photo" :src="cut_src" mode="widthFix"/>
  8. </view>
  9. <view v-if="cut_src" class="fyj_footer text-center">
  10. <button class="fyj_footerBtn fyj_sure" @click='sure'>确定</button>
  11. </view>
  12. </view>
  13. </template>
  14. <script setup>
  15. import {ref} from 'vue'
  16. import cropper from '../components/cropper/index.vue'
  17. const cut_src = ref('');
  18. const cutWidth = ref(0);
  19. const cutHeight = ref(0);
  20. const getCutsrc = (e) => {
  21. console.log('子组件的传值', e)
  22. cut_src.value = e.cut_src;
  23. cutWidth.value = e.cutWidth;
  24. cutHeight.value = e.cutHeight;
  25. }
  26. </script>
  27. <style scoped lang="scss">
  28. .fyj_footer {
  29. margin-top: 20rpx 0;
  30. }
  31. .fyj_footerBtn {
  32. width: 100%;
  33. display: inline-block;
  34. color: #fff;
  35. border-radius: 0;
  36. font-size: 32rpx;
  37. }
  38. .fyj_sure {
  39. background: #fc6b47;
  40. }
  41. .fyj_cutDiv {
  42. margin: 20rpx 0;
  43. }
  44. </style>

参考:

https://www.jb51.net/article/249152.htm

https://blog.csdn.net/hanjiepo/article/details/132428196

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/565230
推荐阅读