康巴易测肤/伤疤uniapp小程序类
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

432 lines
14KB

  1. <template>
  2. <view class="container">
  3. <view
  4. class="image-box"
  5. :style="{
  6. backgroundImage: 'url('+ src +')',
  7. width: imageBoxWidth + 'px',
  8. height: imageBoxHeight + 'px',
  9. borderRadius: cropShape == 'rect' ? '' : ''
  10. }">
  11. <view class="mask"></view>
  12. <view
  13. class="cropping-box"
  14. @touchstart.stop="touchstart"
  15. @touchmove.stop="touchmove"
  16. :style="{
  17. top: cropBoxY + 'px',
  18. left: cropBoxX + 'px',
  19. width: cropBoxWidth + 'px',
  20. height: cropBoxHeight + 'px',
  21. backgroundImage: 'url('+ src +')',
  22. backgroundSize: imageBoxWidth + 'px' + ' ' + imageBoxHeight + 'px',
  23. backgroundPositionX: (-cropBoxX - 2) + 'px',
  24. backgroundPositionY: (-cropBoxY - 2) + 'px',
  25. borderRadius: cropShape == 'rect' ? '' : '50%'
  26. }"
  27. >
  28. <!-- <view v-if="cropShape == 'rect' && !isFixedSize" class="zoom-handle-rect" data-body="handle"></view> -->
  29. <!-- <view v-if="cropShape == 'circular'" class="zoom-chandle-circular" data-body="handle"></view> -->
  30. </view>
  31. </view>
  32. <!-- <canvas class="canvas" canvas-id="canvas" id="canvas" :style="{width: canvasWidth + 'px', height: canvasHeight + 'px'}"></canvas> -->
  33. <view class="operation-area">
  34. <!-- <view class="ratio-area" v-if="cropShape == 'rect'">
  35. <view class="ratio-btn" @click="setRatio('full')">full</view>
  36. <view class="ratio-btn" @click="setRatio(item)" v-for="item in ratioGroup">{{item}}</view>
  37. </view> -->
  38. <view class="crop-area">
  39. <!-- <u--image :src="cropImgPath" mode="widthFix" shape="circle" width="100px"
  40. height="100px"></u--image> -->
  41. </view>
  42. <view class="action">
  43. <!-- <view shape="circle" hover-class="none" class="btn" @click="cancel">取消</view> -->
  44. <view shape="circle" hover-class="none" class="btn full" @click="cropping">继续</view>
  45. </view>
  46. <!-- <view class="bottom">
  47. <u-button size="mini" @click="cancel">取消</u-button>
  48. <u-button size="mini" type="primary" @click="cropping">裁剪</u-button>
  49. </view> -->
  50. </view>
  51. </view>
  52. </template>
  53. <script>
  54. let startX, startY;
  55. let boxX, boxY, boxWidth, boxHeight;
  56. let imageOriginalWidth, imageOriginalHeight;
  57. let imageZoomRatio;
  58. let windowInfo;
  59. export default {
  60. name:'imageCropping',
  61. props: {
  62. src: {
  63. type: String,
  64. },
  65. ratioGroup: {
  66. type: Array,
  67. default(){
  68. return []
  69. }
  70. },
  71. isFixedSize: {
  72. type: Boolean,
  73. default:false
  74. },
  75. cropShape: {
  76. type: String,
  77. default: 'circular' // 'rect' , 'circular'
  78. }
  79. },
  80. data() {
  81. return {
  82. imageBoxWidth: 0,
  83. imageBoxHeight: 0,
  84. cropBoxX: 0,
  85. cropBoxY: 0,
  86. cropBoxWidth: 0,
  87. cropBoxHeight: 0,
  88. // move: 移动 , zoom: 缩放
  89. mode: '',
  90. cropSize: 100,
  91. cropImgPath: ''
  92. }
  93. },
  94. mounted(){
  95. this.getImageInfo();
  96. },
  97. methods:{
  98. // 获取图片尺寸
  99. getImageInfo() {
  100. windowInfo = uni.getWindowInfo();
  101. if (this.src) {
  102. uni.getImageInfo({
  103. src: this.src,
  104. success: (res) => {
  105. console.log("图片尺寸信息", res);
  106. imageOriginalWidth = res.width;
  107. imageOriginalHeight = res.height;
  108. let widthZoomRatio = imageOriginalWidth / windowInfo.windowWidth;
  109. let heightZoomRatio = imageOriginalHeight / windowInfo.windowHeight;
  110. imageZoomRatio = widthZoomRatio > heightZoomRatio ? widthZoomRatio : heightZoomRatio;
  111. this.imageBoxWidth = (imageOriginalWidth / imageZoomRatio);
  112. this.imageBoxHeight = (imageOriginalHeight / imageZoomRatio);
  113. this.setRatio('1:1');
  114. },
  115. fail: (error) => {
  116. console.log("error", error);
  117. }
  118. })
  119. }
  120. },
  121. touchstart(event){
  122. startX = event.touches[0].clientX;
  123. startY = event.touches[0].clientY;
  124. if(event.target.dataset.body == "handle"){
  125. this.mode = 'zoom';
  126. boxWidth = this.cropBoxWidth;
  127. boxHeight = this.cropBoxHeight;
  128. }else {
  129. this.mode = 'move';
  130. boxX = this.cropBoxX;
  131. boxY = this.cropBoxY;
  132. }
  133. },
  134. touchmove(event){
  135. let distanceX = event.touches[0].clientX - startX;
  136. let distanceY = event.touches[0].clientY - startY;
  137. let x = boxX + distanceX;
  138. let y = boxY + distanceY;
  139. let width = boxWidth + distanceX;
  140. let height = boxHeight + distanceY;
  141. let maxX = this.imageBoxWidth-this.cropBoxWidth;
  142. let maxY = this.imageBoxHeight-this.cropBoxHeight;
  143. let maxWidth = this.imageBoxWidth-this.cropBoxX;
  144. let maxHeight = this.imageBoxHeight-this.cropBoxY;
  145. switch (this.mode) {
  146. case 'move':
  147. this.cropBoxX = x < 0 ? 0 : (x > maxX ? maxX : x);
  148. this.cropBoxY = y < 0 ? 0 : (y > maxY ? maxY : y);
  149. break;
  150. case 'zoom':
  151. if(this.cropShape == 'rect'){
  152. this.cropBoxWidth = width > maxWidth ? maxWidth : width;
  153. this.cropBoxHeight = height > maxHeight ? maxHeight : height;
  154. }else{
  155. this.cropBoxHeight = height > maxHeight ? maxHeight : height;
  156. this.cropBoxWidth = height > maxHeight ? maxHeight : height;
  157. }
  158. break;
  159. }
  160. },
  161. setRatio(ratio){
  162. if(ratio == 'full'){
  163. this.cropBoxWidth = this.imageBoxWidth;
  164. this.cropBoxHeight = this.imageBoxHeight;
  165. this.cropBoxX = 0;
  166. this.cropBoxY = 0;
  167. }else{
  168. let x = ratio.split(':')[0];
  169. let y = ratio.split(':')[1];
  170. let r1 = (x/y);
  171. let r2 = (y/x);
  172. if(this.imageBoxWidth < this.imageBoxHeight){
  173. console.log("盒子宽度小于盒子高度");
  174. let size = this.imageBoxWidth;
  175. if(size / r1 > this.imageBoxHeight){
  176. size = this.imageBoxHeight;
  177. this.cropBoxWidth = (size / r2) - this.cropSize;
  178. this.cropBoxHeight = size - this.cropSize;
  179. this.cropBoxX = this.cropSize / 2;
  180. this.cropBoxY = (this.imageBoxWidth - this.imageBoxWidth) / 2;
  181. }else{
  182. this.cropBoxWidth = size - this.cropSize;
  183. this.cropBoxHeight = (size / r1) - this.cropSize;
  184. this.cropBoxX = this.cropSize / 2;
  185. this.cropBoxY = (this.imageBoxHeight - this.cropBoxHeight) / 2;
  186. }
  187. }else{
  188. let size = this.imageBoxHeight;
  189. if(size / r1 > this.imageBoxWidth){
  190. size = this.imageBoxWidth;
  191. this.cropBoxWidth = size - this.cropSize;
  192. this.cropBoxHeight = (size / r2) - this.cropSize;
  193. this.cropBoxX = ((this.imageBoxWidth - this.cropBoxWidth) / 2);
  194. this.cropBoxY = ((this.imageBoxHeight - this.cropBoxHeight) / 2);
  195. }else{
  196. this.cropBoxWidth =( size / r1) - this.cropSize;
  197. this.cropBoxHeight = size - this.cropSize;
  198. this.cropBoxX = ((this.imageBoxWidth - this.cropBoxWidth) / 2);
  199. this.cropBoxY = ((this.imageBoxWidth - this.cropBoxWidth) / 2);
  200. }
  201. }
  202. }
  203. },
  204. async cropping(){
  205. let x = this.cropBoxX * imageZoomRatio;
  206. let y = this.cropBoxY * imageZoomRatio;
  207. let w = this.cropBoxWidth * imageZoomRatio;
  208. let h = this.cropBoxHeight * imageZoomRatio;
  209. // 方式2:
  210. const canvas = uni.createOffscreenCanvas({type: '2d',width: w, height: h});
  211. const context = canvas.getContext('2d');
  212. context.fillStyle = '#fff'; // 设置绘制后的填充颜色
  213. context.globalCompositeOperation = 'source-over';
  214. const image = canvas.createImage();
  215. await new Promise((resolve,reject) => {
  216. image.onload = resolve
  217. image.onerror = reject
  218. image.src = this.src;
  219. image.backgroundColor = 'red'
  220. // image.src = "https://pic4.ntimg.cn/file/20191225/30544261_221404258307_1.jpg";
  221. })
  222. context.clearRect(0, 0, w, h);
  223. if(this.cropShape == 'circular'){
  224. context.beginPath();
  225. context.arc(w/2,h/2,w/2,0,Math.PI * 2);
  226. context.fill();
  227. context.clip();
  228. context.clip();
  229. }
  230. context.drawImage(image,x,y,w,h,0, 0, w, h);
  231. const DataURL = canvas.toDataURL();
  232. const fs = wx.getFileSystemManager();
  233. let tempFilePath = `${wx.env.USER_DATA_PATH}/` + new Date().getTime() + '.png'
  234. fs.writeFile({
  235. filePath: tempFilePath,
  236. data: DataURL.replace('data:image/png;base64,',''),
  237. encoding: 'base64',
  238. success: res => {
  239. this.$emit('completed',{
  240. tempFilePath
  241. });
  242. this.cropImgPath = tempFilePath;
  243. },
  244. fail(res) {
  245. console.error(res)
  246. }
  247. })
  248. // 方式一:
  249. // const context = uni.createCanvasContext('canvas', this)
  250. // context.drawImage(this.src,x,y,w,h,0, 0, w, h)
  251. // context.draw(false, ()=>{
  252. // console.log(123);
  253. // uni.canvasToTempFilePath({
  254. // canvas: context,
  255. // success: function(res) {
  256. // console.log(res)
  257. // },
  258. // complete: function(res){
  259. // console.log(res)
  260. // }
  261. // },this)
  262. // })
  263. },
  264. yuantu(){
  265. this.$emit('completed',{
  266. tempFilePath: this.src
  267. })
  268. },
  269. cancel(){
  270. this.$emit('cancel')
  271. }
  272. }
  273. }
  274. </script>
  275. <style lang="scss" scoped>
  276. .container{
  277. width: 100vw;
  278. height: 100vh;
  279. overflow: hidden;
  280. position: fixed;
  281. left: 0;
  282. top: 0;
  283. z-index: 99;
  284. display: flex;
  285. align-items: flex-start;
  286. justify-content: flex-start;
  287. background-color: #fff;
  288. .image-box{
  289. position: relative;
  290. background-size: contain;
  291. background-repeat: no-repeat;
  292. // filter: invert(1);
  293. z-index: 999;
  294. .mask{
  295. width: 100%;
  296. height: 100%;
  297. position: absolute;
  298. left: 0;
  299. top: 0;
  300. background-color: rgba(0, 0, 0, 0.4);
  301. z-index: 99;
  302. /* border-radius: 50%; */
  303. // backdrop-filter: blur(10px);
  304. }
  305. .cropping-box{
  306. position: absolute;
  307. border: 2px solid springgreen;
  308. box-sizing: border-box;
  309. z-index: 9999;
  310. // .zoom-handle-rect,
  311. // .zoom-chandle-circular{
  312. // width: 10px;
  313. // height: 10px;
  314. // background-color: springgreen;
  315. // border-radius: 50%;
  316. // position: absolute;
  317. // right: -6px;
  318. // bottom: -6px;
  319. // }
  320. .zoom-chandle-circular{
  321. right: 50%;
  322. transform: translateX(5px);
  323. }
  324. }
  325. }
  326. }
  327. .operation-area{
  328. position: fixed;
  329. bottom: 0;
  330. z-index: 1;
  331. width: 100%;
  332. padding: 10rpx 0;
  333. display: flex;
  334. flex-direction: column;
  335. align-items: center;
  336. justify-content: center;
  337. .ratio-area{
  338. display: flex;
  339. align-items: center;
  340. justify-content: center;
  341. margin-bottom: 16rpx;
  342. .ratio-btn{
  343. display: inline-block;
  344. padding: 8rpx 20rpx;
  345. border: 2rpx solid white;
  346. color: white;
  347. margin: 0 10rpx;
  348. font-size: 12px;
  349. }
  350. }
  351. .bottom{
  352. height: 120rpx;
  353. width: 100%;
  354. display: flex;
  355. align-items: center;
  356. justify-content: center;
  357. button{
  358. display: inline-block;
  359. margin: 0 20rpx;
  360. }
  361. }
  362. .action {
  363. height: 120rpx;
  364. width: 100vw;
  365. position: fixed;
  366. bottom: 60rpx;
  367. left: 0;
  368. display: flex;
  369. justify-content: center;
  370. align-items: center;
  371. background: none;
  372. z-index: 9999;
  373. .btn {
  374. height: 89rpx;
  375. line-height: 89rpx;
  376. width: 266rpx;
  377. border: 4rpx solid #7F66E8;
  378. text-align: center;
  379. border-radius: 60rpx;
  380. font-family: OPPOSans;
  381. font-weight: bold;
  382. font-size: 36rpx;
  383. color: #7F66E8;
  384. &.full {
  385. background-color: #7F66E8;
  386. color: #FFFFFF;
  387. margin-left: 30rpx;
  388. font-weight: bold;
  389. }
  390. }
  391. }
  392. }
  393. .canvas{
  394. position: absolute;
  395. bottom: 0;
  396. left: 0;
  397. z-index: 1;
  398. border: 1px solid;
  399. // display: none;
  400. }
  401. </style>