You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1274 lines
39 KiB

  1. <template>
  2. <view class="u-cropper">
  3. <!-- <image :src="imgSrc.imgSrc" @click="select" :style="[ imgStyle ]" class="my-avatar"></image> -->
  4. <up-canvas ref="avatarCanvas" :canvas-id="'avatar-canvas-' + instanceId" class="my-canvas" :width="windowWidth || 0" :height="windowHeight || 0" :style="{ top: styleTop, height: cvsStyleHeight }" bg-color="transparent"></up-canvas>
  5. <up-canvas ref="operCanvas" :canvas-id="'oper-canvas-' + instanceId" class="oper-canvas" :width="windowWidth || 0" :height="windowHeight || 0" :style="{ top: styleTop, height: cvsStyleHeight }" bg-color="transparent" @touchstart="start" @touchmove="move" @touchend="end"></up-canvas>
  6. <up-canvas ref="prvCanvas" :canvas-id="'prv-canvas-' + instanceId" class="prv-canvas" :width="windowWidth || 0" :height="windowHeight || 0" bg-color="transparent" @touchstart="hideImg" :style="{ height: cvsStyleHeight, top: prvTop }"></up-canvas>
  7. <view class="oper-wrapper" :style="{ display: styleDisplay }">
  8. <view class="oper">
  9. <view class="btn-wrapper" v-if="showOper">
  10. <view @click="select" hover-class="hover" :style="{ width: btnWidth }">
  11. <text>{{ t("up.common.re-select") }}</text>
  12. </view>
  13. <view @click="close" hover-class="hover" :style="{ width: btnWidth }">
  14. <text>{{ t("up.common.close") }}</text>
  15. </view>
  16. <view @click="rotate" hover-class="hover" :style="{ width: btnWidth, display: btnDsp }">
  17. <text>{{ t("up.common.rotate") }}</text>
  18. </view>
  19. <view @click="preview" hover-class="hover" :style="{ width: btnWidth }">
  20. <text>{{ t("up.common.preview") }}</text>
  21. </view>
  22. <view @click="confirm" hover-class="hover" :style="{ width: btnWidth }">
  23. <text>{{ t("up.common.confirm") }}</text>
  24. </view>
  25. </view>
  26. <view class="clr-wrapper" v-else>
  27. <slider class="my-slider" @change="colorChange" block-size="25" value="0" min="-100" max="100" activeColor="red" backgroundColor="green" block-color="grey" show-value></slider>
  28. <view @click="prvUpload" hover-class="hover" :style="{ width: btnWidth }">
  29. <text>{{ t("up.common.confirm") }}</text>
  30. </view>
  31. </view>
  32. </view>
  33. </view>
  34. <view @click="chooseImage(0, {})" v-if="styleDisplay == 'none'">
  35. <slot>
  36. </slot>
  37. </view>
  38. </view>
  39. </template>
  40. <script>
  41. import { t } from '../../libs/i18n'
  42. const tabHeight = 50;
  43. export default {
  44. name: "u-cropper",
  45. data() {
  46. return {
  47. // 添加实例ID用于区分不同实例
  48. instanceId: Date.now() + '-' + Math.random().toString(36).substr(2, 9),
  49. cvsStyleHeight: '0px',
  50. styleDisplay: 'none',
  51. styleTop: '-10000px',
  52. prvTop: '-10000px',
  53. imgStyle: {},
  54. selStyle: {},
  55. showOper: true,
  56. imgSrc: {
  57. imgSrc: ''
  58. },
  59. btnWidth: '19%',
  60. btnDsp: 'flex',
  61. // 裁剪区域宽度,用于设置选择区域的宽度
  62. arWidth: '',
  63. // 裁剪区域高度,用于设置选择区域的高度
  64. arHeight: '',
  65. // 导出图片宽度,用于设置最终导出图片的宽度
  66. expWidth: '',
  67. // 导出图片高度,用于设置最终导出图片的高度
  68. expHeight: '',
  69. // 是否允许调整裁剪框大小
  70. letChangeSize: false,
  71. // 底部安全区大小
  72. safeAreaInsetsBottom: 0,
  73. };
  74. },
  75. watch: {
  76. avatarSrc() {
  77. this.imgSrc.imgSrc = this.avatarSrc;
  78. }
  79. },
  80. emits: ['avtinit', 'confirm'],
  81. props: {
  82. minScale: '',
  83. maxScale: '',
  84. canScale: true,
  85. canRotate: true,
  86. lockWidth: '',
  87. lockHeight: '',
  88. stretch: '',
  89. lock: '',
  90. noTab: true,
  91. inner: false,
  92. quality: '',
  93. index: '',
  94. canChangeSize: false,
  95. areaWidth: '300rpx',
  96. // 裁剪区域高度,用于设置选择区域的高度
  97. areaHeight: '300rpx',
  98. // 导出图片宽度,用于设置最终导出图片的宽度
  99. exportWidth: '260rpx',
  100. // 导出图片高度,用于设置最终导出图片的高度
  101. exportHeight: '260rpx',
  102. // 画布填充色,默认透明,可传 'black'、'#ffffff' 等
  103. fillColor: {
  104. type: String,
  105. default: 'transparent'
  106. },
  107. },
  108. created() {
  109. this.ctxCanvas = null;
  110. this.ctxCanvasOper = null;
  111. this.ctxCanvasPrv = null;
  112. this.qlty = parseInt(this.quality) || 0.9;
  113. this.imgSrc.imgSrc = this.imageSrc;
  114. this.letRotate = (this.canRotate === false || this.inner === true) ? 0 : 1;
  115. this.letScale = this.canScale === false ? 0 : 1;
  116. // 是否允许调整裁剪框大小,false表示不允许,其他值表示允许
  117. this.letChangeSize = this.canChangeSize;
  118. this.isin = this.inner === true ? 1 : 0;
  119. this.indx = this.index || undefined;
  120. this.mnScale = this.minScale || 0.3;
  121. this.mxScale = this.maxScale || 4;
  122. this.noBar = this.noTab === true ? 1 : 0;
  123. this.stc = this.stretch;
  124. this.lck = this.lock;
  125. if (this.isin) {
  126. this.btnWidth = '24%';
  127. this.btnDsp = 'none';
  128. } else {
  129. this.btnWidth = '19%';
  130. this.btnDsp = 'flex';
  131. }
  132. if (this.noBar) {
  133. this.moreHeight = 0;
  134. this.windowResize();
  135. } else {
  136. uni.showTabBar({
  137. complete: (res) => {
  138. this.moreHeight = (res.errMsg === 'showTabBar:ok') ? 50 : 0;
  139. this.windowResize();
  140. }
  141. });
  142. }
  143. },
  144. mounted() {
  145. this.initCanvasRefs(true);
  146. },
  147. methods: {
  148. t,
  149. async initCanvasRefs(force = false) {
  150. await this.$nextTick();
  151. const avatarCanvas = this.$refs.avatarCanvas;
  152. const operCanvas = this.$refs.operCanvas;
  153. const prvCanvas = this.$refs.prvCanvas;
  154. if (!avatarCanvas || !operCanvas || !prvCanvas) return false;
  155. await Promise.all([
  156. avatarCanvas.initCanvas(force),
  157. operCanvas.initCanvas(force),
  158. prvCanvas.initCanvas(force)
  159. ]);
  160. this.ctxCanvas = avatarCanvas;
  161. this.ctxCanvasOper = operCanvas;
  162. this.ctxCanvasPrv = prvCanvas;
  163. return true;
  164. },
  165. windowResize() {
  166. let sysInfo = uni.getSystemInfoSync();
  167. this.platform = sysInfo.platform;
  168. this.pixelRatio = sysInfo.pixelRatio;
  169. this.windowWidth = sysInfo.windowWidth;
  170. this.safeAreaInsetsBottom = sysInfo.safeAreaInsets.bottom;
  171. // #ifdef H5
  172. this.drawTop = sysInfo.windowTop;
  173. this.windowHeight = sysInfo.windowHeight + sysInfo.windowBottom;
  174. this.cvsStyleHeight = this.windowHeight - tabHeight + 'px';
  175. // #endif
  176. // #ifdef APP-PLUS
  177. if (this.platform === 'android') {
  178. this.windowHeight = sysInfo.screenHeight + sysInfo.statusBarHeight;
  179. this.cvsStyleHeight = this.windowHeight - tabHeight + 'px';
  180. } else {
  181. this.windowHeight = sysInfo.windowHeight + this.moreHeight;
  182. this.cvsStyleHeight = this.windowHeight - tabHeight - this.safeAreaInsetsBottom + 6 + 'px';
  183. }
  184. // #endif
  185. // #ifdef MP
  186. this.windowHeight = sysInfo.windowHeight + this.moreHeight;
  187. this.cvsStyleHeight = this.windowHeight - tabHeight - this.safeAreaInsetsBottom - 2 + 'px';
  188. // #endif
  189. this.pxRatio = this.windowWidth / 750;
  190. let style = this.avatarStyle;
  191. if (style && style !== true && (style = style.trim())) {
  192. style = style.split(';');
  193. let obj = {};
  194. for (let v of style) {
  195. if (!v) continue;
  196. v = v.trim().split(':');
  197. if (v[1].indexOf('rpx') >= 0) {
  198. let arr = v[1].trim().split(' ');
  199. for (let k in arr) {
  200. if (!arr[k]) continue;
  201. if (arr[k].indexOf('rpx') >= 0) {
  202. arr[k] = parseFloat(arr[k]) * this.pxRatio + 'px';
  203. }
  204. }
  205. v[1] = arr.join(' ');
  206. }
  207. obj[v[0].trim()] = v[1].trim();
  208. }
  209. this.imgStyle = obj;
  210. }
  211. this.expWidth && (this.expWidth = this.expWidth.indexOf('rpx') >= 0 ? parseInt(this.expWidth) * this.pxRatio : parseInt(this.expWidth));
  212. this.expHeight && (this.expHeight = this.expHeight.indexOf('rpx') >= 0 ? parseInt(this.expHeight) * this.pxRatio : parseInt(this.expHeight));
  213. if (this.styleDisplay === 'flex') {
  214. this.drawInit(true);
  215. }
  216. this.hideImg();
  217. },
  218. select() {
  219. if (this.fSelecting) return;
  220. this.fSelecting = true;
  221. setTimeout(() => { this.fSelecting = false; }, 500);
  222. const self = this
  223. uni.chooseImage({
  224. count: 1,
  225. sizeType: ['original', 'compressed'],
  226. sourceType: ['album', 'camera'],
  227. success: (r) => {
  228. uni.showLoading({ mask: true });
  229. let path = this.imgPath = r.tempFilePaths[0];
  230. uni.getImageInfo({
  231. src: path,
  232. success: r => {
  233. this.imgWidth = r.width;
  234. this.imgHeight = r.height;
  235. this.path = path;
  236. if (!this.hasSel) {
  237. let style = this.selStyle || {};
  238. if (this.arWidth && this.arHeight) {
  239. let areaWidth = this.arWidth.indexOf('rpx') >= 0 ? parseInt(this.arWidth) * this.pxRatio : parseInt(this.arWidth),
  240. areaHeight = this.arHeight.indexOf('rpx') >= 0 ? parseInt(this.arHeight) * this.pxRatio : parseInt(this.arHeight);
  241. style.width = areaWidth + 'px';
  242. style.height = areaHeight + 'px';
  243. style.top = (this.windowHeight - areaHeight - tabHeight) / 2 + 'px';
  244. style.left = (this.windowWidth - areaWidth) / 2 + 'px';
  245. } else {
  246. uni.showModal({
  247. title: t("up.cropper.emptyWidhtOrHeight"),
  248. showCancel: false
  249. })
  250. return;
  251. }
  252. this.selStyle = style;
  253. }
  254. if (this.noBar) {
  255. this.drawInit(true);
  256. } else {
  257. uni.hideTabBar({
  258. complete: () => {
  259. this.drawInit(true);
  260. }
  261. });
  262. }
  263. },
  264. fail: () => {
  265. uni.showToast({
  266. title: "error3",
  267. duration: 2000,
  268. })
  269. },
  270. complete() {
  271. uni.hideLoading();
  272. }
  273. });
  274. }, fail(err) {
  275. self.$emit('cancel')
  276. }
  277. })
  278. },
  279. async confirm() {
  280. if (this.fUploading) return;
  281. this.fUploading = true;
  282. setTimeout(() => { this.fUploading = false; }, 1000)
  283. let style = this.selStyle,
  284. x = parseInt(style.left),
  285. y = parseInt(style.top),
  286. width = parseInt(style.width),
  287. height = parseInt(style.height),
  288. expWidth = this.expWidth || width,
  289. expHeight = this.expHeight || height;
  290. // #ifdef H5
  291. // x *= this.pixelRatio;
  292. // y *= this.pixelRatio;
  293. expWidth = width;
  294. expHeight = height;
  295. // #endif
  296. uni.showLoading({ mask: true });
  297. this.styleDisplay = 'none';
  298. this.styleTop = '-10000px';
  299. this.hasSel = false;
  300. this.hideImg();
  301. await this.initCanvasRefs();
  302. this.ctxCanvas.toTempFilePath({
  303. x: x,
  304. y: y,
  305. width: width,
  306. height: height,
  307. destWidth: expWidth,
  308. destHeight: expHeight,
  309. fileType: 'png',
  310. quality: this.qlty,
  311. success: (r) => {
  312. r = r.tempFilePath;
  313. // #ifdef H5
  314. this.btop(r).then(async (r) => {
  315. if (this.expWidth && this.expHeight) {
  316. let ctxCanvas = this.ctxCanvas;
  317. expWidth = this.expWidth,
  318. expHeight = this.expHeight;
  319. await ctxCanvas.drawImage(r, 0, 0, expWidth, expHeight);
  320. ctxCanvas.draw(false, () => {
  321. ctxCanvas.toTempFilePath({
  322. x: 0,
  323. y: 0,
  324. width: expWidth,
  325. height: expHeight,
  326. destWidth: expWidth,
  327. destHeight: expHeight,
  328. fileType: 'png',
  329. quality: this.qlty,
  330. success: (r) => {
  331. r = r.tempFilePath;
  332. this.btop(r).then((r) => {
  333. this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
  334. });
  335. },
  336. fail: () => {
  337. uni.showToast({
  338. title: "error0",
  339. duration: 2000,
  340. })
  341. }
  342. });
  343. });
  344. } else {
  345. this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
  346. }
  347. })
  348. // #endif
  349. // #ifndef H5
  350. this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
  351. // #endif
  352. },
  353. fail: (res) => {
  354. uni.showToast({
  355. title: "error1",
  356. duration: 2000,
  357. })
  358. },
  359. complete: () => {
  360. uni.hideLoading();
  361. this.noBar || uni.showTabBar();
  362. }
  363. });
  364. },
  365. // 用户点击"预览"模式下的"确认"按钮时被调用,用于将预览的裁剪结果上传
  366. async prvUpload() {
  367. if (this.fPrvUploading) return;
  368. this.fPrvUploading = true;
  369. setTimeout(() => { this.fPrvUploading = false; }, 1000)
  370. let style = this.selStyle,
  371. destWidth = parseInt(style.width),
  372. destHeight = parseInt(style.height),
  373. prvX = this.prvX,
  374. prvY = this.prvY,
  375. prvWidth = this.prvWidth,
  376. prvHeight = this.prvHeight,
  377. expWidth = this.expWidth || prvWidth,
  378. expHeight = this.expHeight || prvHeight;
  379. // #ifdef H5
  380. // prvX *= this.pixelRatio;
  381. // prvY *= this.pixelRatio;
  382. expWidth = prvWidth;
  383. expHeight = prvHeight;
  384. // #endif
  385. uni.showLoading({ mask: true });
  386. this.styleDisplay = 'none';
  387. this.styleTop = '-10000px';
  388. this.hasSel = false;
  389. this.hideImg();
  390. await this.initCanvasRefs();
  391. this.ctxCanvasPrv.toTempFilePath({
  392. x: prvX,
  393. y: prvY,
  394. width: prvWidth,
  395. height: prvHeight,
  396. destWidth: expWidth,
  397. destHeight: expHeight,
  398. fileType: 'png',
  399. quality: this.qlty,
  400. success: (r) => {
  401. r = r.tempFilePath;
  402. // #ifdef H5
  403. this.btop(r).then(async (r) => {
  404. if (this.expWidth && this.expHeight) {
  405. let ctxCanvas = this.ctxCanvas;
  406. expWidth = this.expWidth,
  407. expHeight = this.expHeight;
  408. await ctxCanvas.drawImage(r, 0, 0, expWidth, expHeight);
  409. ctxCanvas.draw(false, () => {
  410. ctxCanvas.toTempFilePath({
  411. x: 0,
  412. y: 0,
  413. width: expWidth,
  414. height: expHeight,
  415. destWidth: expWidth,
  416. destHeight: expHeight,
  417. fileType: 'png',
  418. quality: this.qlty,
  419. success: (r) => {
  420. r = r.tempFilePath;
  421. this.btop(r).then((r) => {
  422. this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
  423. });
  424. },
  425. fail: () => {
  426. uni.showToast({
  427. title: "error0",
  428. duration: 2000,
  429. })
  430. }
  431. });
  432. });
  433. } else {
  434. this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
  435. }
  436. })
  437. // #endif
  438. // #ifndef H5
  439. this.$emit("confirm", { avatar: this.imgSrc, path: r, index: this.indx, data: this.rtn });
  440. // #endif
  441. },
  442. fail: () => {
  443. uni.showToast({
  444. title: "error_prv",
  445. duration: 2000,
  446. })
  447. },
  448. complete: () => {
  449. uni.hideLoading();
  450. this.noBar || uni.showTabBar();
  451. }
  452. });
  453. },
  454. async drawInit(ini = false) {
  455. await this.initCanvasRefs();
  456. let allWidth = this.windowWidth,
  457. allHeight = this.windowHeight,
  458. imgWidth = this.imgWidth,
  459. imgHeight = this.imgHeight,
  460. imgRadio = imgWidth / imgHeight,
  461. useWidth = allWidth - 40,
  462. useHeight = allHeight - tabHeight - 80,
  463. pixelRatio = this.pixelRatio,
  464. selWidth = parseInt(this.selStyle.width),
  465. selHeight = parseInt(this.selStyle.height);
  466. this.fixWidth = 0;
  467. this.fixHeight = 0;
  468. this.lckWidth = 0;
  469. this.lckHeight = 0;
  470. switch (this.stc) {
  471. case 'x': this.fixWidth = 1; break;
  472. case 'y': this.fixHeight = 1; break;
  473. case 'long': if (imgRadio > 1) this.fixWidth = 1; else this.fixHeight = 1; break;
  474. case 'short': if (imgRadio > 1) this.fixHeight = 1; else this.fixWidth = 1; break;
  475. case 'longSel': if (selWidth > selHeight) this.fixWidth = 1; else this.fixHeight = 1; break;
  476. case 'shortSel': if (selWidth > selHeight) this.fixHeight = 1; else this.fixWidth = 1; break;
  477. }
  478. // lck 用于控制裁剪框的宽度和高度锁定行为
  479. // 'x': 锁定宽度,不允许水平方向调整
  480. // 'y': 锁定高度,不允许垂直方向调整
  481. // 'long': 根据图片长边锁定,如果图片横向较长则锁定宽度,否则锁定高度
  482. // 'short': 根据图片短边锁定,如果图片横向较长则锁定高度,否则锁定宽度
  483. // 'longSel': 根据选择框的长边锁定,如果选择框宽度大于高度则锁定宽度,否则锁定高度
  484. // 'shortSel': 根据选择框的短边锁定,如果选择框宽度大于高度则锁定高度,否则锁定宽度
  485. switch (this.lck) {
  486. case 'x': this.lckWidth = 1; break;
  487. case 'y': this.lckHeight = 1; break;
  488. case 'long': if (imgRadio > 1) this.lckWidth = 1; else this.lckHeight = 1; break;
  489. case 'short': if (imgRadio > 1) this.lckHeight = 1; else this.lckWidth = 1; break;
  490. case 'longSel': if (selWidth > selHeight) this.lckWidth = 1; else this.lckHeight = 1; break;
  491. case 'shortSel': if (selWidth > selHeight) this.lckHeight = 1; else this.lckWidth = 1; break;
  492. }
  493. if (this.fixWidth) {
  494. useWidth = selWidth;
  495. useHeight = useWidth / imgRadio;
  496. } else if (this.fixHeight) {
  497. useHeight = selHeight;
  498. useWidth = useHeight * imgRadio;
  499. } else if (imgRadio < 1) {
  500. if (imgHeight < useHeight) {
  501. useWidth = imgWidth;
  502. useHeight = imgHeight;
  503. } else {
  504. useHeight = useHeight;
  505. useWidth = useHeight * imgRadio;
  506. }
  507. } else {
  508. if (imgWidth < useWidth) {
  509. useWidth = imgWidth;
  510. useHeight = imgHeight;
  511. } else {
  512. useWidth = useWidth;
  513. useHeight = useWidth / imgRadio;
  514. }
  515. }
  516. if (this.isin) {
  517. this.scaleWidth = 0;
  518. this.scaleHeight = 0;
  519. if (useWidth < selWidth) {
  520. useWidth = selWidth;
  521. useHeight = useWidth / imgRadio;
  522. this.lckHeight = 0;
  523. }
  524. if (useHeight < selHeight) {
  525. useHeight = selHeight;
  526. useWidth = useHeight * imgRadio;
  527. this.lckWidth = 0;
  528. }
  529. }
  530. this.scaleSize = 1;
  531. this.rotateDeg = 0;
  532. this.posWidth = (allWidth - useWidth) / 2;
  533. this.posHeight = (allHeight - useHeight - tabHeight) / 2;
  534. this.useWidth = useWidth;
  535. this.useHeight = useHeight;
  536. let style = this.selStyle,
  537. left = parseInt(style.left),
  538. top = parseInt(style.top),
  539. width = parseInt(style.width),
  540. height = parseInt(style.height),
  541. canvas = this.canvas,
  542. canvasOper = this.canvasOper,
  543. ctxCanvas = this.ctxCanvas,
  544. ctxCanvasOper = this.ctxCanvasOper;
  545. ctxCanvasOper.setLineWidth(3);
  546. ctxCanvasOper.setStrokeStyle('grey');
  547. ctxCanvasOper.setGlobalAlpha(0.4);
  548. ctxCanvasOper.setFillStyle('black');
  549. ctxCanvasOper.strokeRect(left, top, width, height);
  550. ctxCanvasOper.fillRect(0, 0, this.windowWidth, top);
  551. ctxCanvasOper.fillRect(0, top, left, height);
  552. ctxCanvasOper.fillRect(0, top + height, this.windowWidth, this.windowHeight - height - top - tabHeight);
  553. ctxCanvasOper.fillRect(left + width, top, this.windowWidth - width - left, height);
  554. ctxCanvasOper.setStrokeStyle('red');
  555. ctxCanvasOper.moveTo(left + 20, top); ctxCanvasOper.lineTo(left, top); ctxCanvasOper.lineTo(left, top + 20);
  556. ctxCanvasOper.moveTo(left + width - 20, top); ctxCanvasOper.lineTo(left + width, top); ctxCanvasOper.lineTo(left + width, top + 20);
  557. ctxCanvasOper.moveTo(left + 20, top + height); ctxCanvasOper.lineTo(left, top + height); ctxCanvasOper.lineTo(left, top + height - 20);
  558. ctxCanvasOper.moveTo(left + width - 20, top + height); ctxCanvasOper.lineTo(left + width, top + height); ctxCanvasOper.lineTo(left + width, top + height - 20);
  559. // 绘制控制点(四个角)
  560. const controlPointSize = 10;
  561. ctxCanvasOper.setFillStyle('white');
  562. ctxCanvasOper.setStrokeStyle('grey');
  563. ctxCanvasOper.setLineWidth(1);
  564. // 左上角
  565. ctxCanvasOper.fillRect(left - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
  566. ctxCanvasOper.strokeRect(left - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
  567. // 右上角
  568. ctxCanvasOper.fillRect(left + width - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
  569. ctxCanvasOper.strokeRect(left + width - controlPointSize / 2, top - controlPointSize / 2, controlPointSize, controlPointSize);
  570. // 左下角
  571. ctxCanvasOper.fillRect(left - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
  572. ctxCanvasOper.strokeRect(left - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
  573. // 右下角
  574. ctxCanvasOper.fillRect(left + width - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
  575. ctxCanvasOper.strokeRect(left + width - controlPointSize / 2, top + height - controlPointSize / 2, controlPointSize, controlPointSize);
  576. ctxCanvasOper.stroke();
  577. ctxCanvasOper.draw(false, () => {
  578. if (ini) {
  579. this.styleDisplay = 'flex';
  580. // #ifdef H5
  581. this.styleTop = this.drawTop + 'px';
  582. // #endif
  583. // #ifndef H5
  584. this.styleTop = '0';
  585. // #endif
  586. if (this.fillColor && this.fillColor !== 'transparent') {
  587. ctxCanvas.setFillStyle(this.fillColor);
  588. }
  589. this.drawImage();
  590. }
  591. });
  592. this.$emit("avtinit");
  593. },
  594. async drawImage() {
  595. await this.initCanvasRefs();
  596. let tm_now = Date.now();
  597. if (tm_now - this.drawTm < 20) return;
  598. this.drawTm = tm_now;
  599. let ctxCanvas = this.ctxCanvas;
  600. if (this.fillColor && this.fillColor !== 'transparent') {
  601. ctxCanvas.fillRect(0, 0, this.windowWidth, this.windowHeight - tabHeight);
  602. }
  603. ctxCanvas.save();
  604. ctxCanvas.translate(this.posWidth + this.useWidth / 2, this.posHeight + this.useHeight / 2);
  605. ctxCanvas.scale(this.scaleSize, this.scaleSize);
  606. ctxCanvas.rotate(this.rotateDeg * Math.PI / 180);
  607. await ctxCanvas.drawImage(this.imgPath, -this.useWidth / 2, -this.useHeight / 2, this.useWidth, this.useHeight);
  608. ctxCanvas.restore();
  609. ctxCanvas.draw(false);
  610. },
  611. hideImg() {
  612. this.prvImg = '';
  613. this.prvTop = '-10000px';
  614. this.showOper = true;
  615. this.prvImgData = null;
  616. this.target = null;
  617. },
  618. close() {
  619. console.log('up-cropper close');
  620. this.styleDisplay = 'none';
  621. this.styleTop = '-10000px';
  622. this.hasSel = false;
  623. this.hideImg();
  624. this.noBar || uni.showTabBar();
  625. this.$emit('cancel');
  626. },
  627. async preview() {
  628. if (this.fPreviewing) return;
  629. this.fPreviewing = true;
  630. setTimeout(() => { this.fPreviewing = false; }, 1000);
  631. let style = this.selStyle,
  632. x = parseInt(style.left),
  633. y = parseInt(style.top),
  634. width = parseInt(style.width),
  635. height = parseInt(style.height);
  636. uni.showLoading({ mask: true });
  637. // console.log('size', x, y, width, height)
  638. await this.initCanvasRefs();
  639. this.ctxCanvas.toTempFilePath({
  640. x: x,
  641. y: y,
  642. width: width,
  643. height: height,
  644. fileType: 'png',
  645. quality: this.qlty,
  646. success: async (r) => {
  647. // console.log(r)
  648. this.prvImgTmp = r = r.tempFilePath;
  649. let ctxCanvasPrv = this.ctxCanvasPrv,
  650. prvX = this.windowWidth,
  651. prvY = parseInt(this.cvsStyleHeight),
  652. prvWidth = parseInt(this.selStyle.width),
  653. prvHeight = parseInt(this.selStyle.height),
  654. useWidth = prvX - 40,
  655. useHeight = prvY - 80,
  656. radio = useWidth / prvWidth,
  657. rHeight = prvHeight * radio;
  658. if (rHeight < useHeight) {
  659. prvWidth = useWidth;
  660. prvHeight = rHeight;
  661. } else {
  662. radio = useHeight / prvHeight;
  663. prvWidth *= radio;
  664. prvHeight = useHeight;
  665. }
  666. if (this.fillColor && this.fillColor !== 'transparent') {
  667. ctxCanvasPrv.setFillStyle(this.fillColor);
  668. ctxCanvasPrv.fillRect(0, 0, prvX, prvY);
  669. ctxCanvasPrv.fillRect(x, y, width, height);
  670. }
  671. this.prvX = prvX = (prvX - prvWidth) / 2;
  672. this.prvY = prvY = (prvY - prvHeight) / 2;
  673. this.prvWidth = prvWidth;
  674. this.prvHeight = prvHeight;
  675. await ctxCanvasPrv.drawImage(r, prvX, prvY, prvWidth, prvHeight);
  676. ctxCanvasPrv.draw(false, () => {
  677. // #ifdef H5
  678. this.btop(this.prvImgTmp).then((r) => {
  679. this.showOper = false;
  680. this.prvTop = this.drawTop + 'px';
  681. })
  682. // #endif
  683. // #ifndef H5
  684. if (this.platform != 'android') {
  685. this.showOper = false;
  686. }
  687. this.prvTop = '0';
  688. // #endif
  689. });
  690. },
  691. fail: () => {
  692. uni.showToast({
  693. title: "error2",
  694. duration: 2000,
  695. })
  696. },
  697. complete: () => {
  698. uni.hideLoading();
  699. }
  700. });
  701. },
  702. chooseImage(index = undefined, params = undefined, data = undefined) {
  703. if (params) {
  704. console.log(params)
  705. let areaWidth = params.areaWidth || this.areaWidth,
  706. areaHeight = params.areaHeight || this.areaHeight,
  707. expWidth = params.exportWidth || this.exportWidth,
  708. expHeight = params.exportHeight || this.exportHeight,
  709. quality = params.quality,
  710. canRotate = params.canRotate,
  711. canScale = params.canScale,
  712. canChangeSize = params.canChangeSize,
  713. minScale = params.minScale,
  714. maxScale = params.maxScale,
  715. stretch = params.stretch,
  716. inner = params.inner,
  717. lock = params.lock;
  718. console.log('areaWidth', this.areaWidth)
  719. expWidth && (this.expWidth = expWidth.indexOf('rpx') >= 0 ? parseInt(expWidth) * this.pxRatio : parseInt(expWidth));
  720. expHeight && (this.expHeight = expHeight.indexOf('rpx') >= 0 ? parseInt(expHeight) * this.pxRatio : parseInt(expHeight));
  721. this.letRotate = canRotate === false ? 0 : 1;
  722. this.letScale = canScale === false ? 0 : 1;
  723. // 设置是否允许调整裁剪框大小
  724. this.letChangeSize = canChangeSize || false;
  725. this.qlty = parseInt(quality) || 0.9;
  726. this.mnScale = minScale || 0.3;
  727. this.mxScale = maxScale || 4;
  728. this.stc = stretch;
  729. this.isin = inner === true ? 1 : 0;
  730. this.lck = lock;
  731. if (this.isin) {
  732. this.btnWidth = '24%';
  733. this.btnDsp = 'none';
  734. } else {
  735. this.btnWidth = '19%';
  736. this.btnDsp = 'flex';
  737. }
  738. if (areaWidth && areaHeight) {
  739. areaWidth = areaWidth.indexOf('rpx') >= 0 ? parseInt(areaWidth) * this.pxRatio : parseInt(areaWidth);
  740. areaHeight = areaHeight.indexOf('rpx') >= 0 ? parseInt(areaHeight) * this.pxRatio : parseInt(areaHeight);
  741. this.selStyle.width = areaWidth + 'px';
  742. this.selStyle.height = areaHeight + 'px';
  743. this.selStyle.top = (this.windowHeight - areaHeight - tabHeight) / 2 + 'px';
  744. this.selStyle.left = (this.windowWidth - areaWidth) / 2 + 'px';
  745. // console.log(this.selStyle);
  746. this.hasSel = true;
  747. }
  748. }
  749. this.rtn = data;
  750. this.indx = index;
  751. this.select();
  752. },
  753. rotate() {
  754. // #ifdef APP-PLUS
  755. if (this.platform === 'android') {
  756. if (this.fRotateing) return;
  757. this.fRotateing = true;
  758. setTimeout(() => { this.fRotateing = false; }, 500);
  759. }
  760. // #endif
  761. // if(this.letRotate) {
  762. this.rotateDeg += 90 - this.rotateDeg % 90;
  763. this.drawImage();
  764. // }
  765. },
  766. start(e) {
  767. let touches = e.touches,
  768. touch0 = touches[0],
  769. touch1 = touches[1];
  770. this.touch0 = touch0;
  771. this.touch1 = touch1;
  772. if (touch1) {
  773. let x = touch1.x - touch0.x,
  774. y = touch1.y - touch0.y;
  775. this.fgDistance = Math.sqrt(x * x + y * y);
  776. } else {
  777. // 只有在允许调整大小时才检查控制点
  778. if (this.letChangeSize) {
  779. // 检查是否点击在控制点上
  780. const controlPointSize = 20;
  781. const x = touch0.x;
  782. const y = touch0.y;
  783. const style = this.selStyle;
  784. const left = parseInt(style.left);
  785. const top = parseInt(style.top);
  786. const width = parseInt(style.width);
  787. const height = parseInt(style.height);
  788. // 检查四个控制点
  789. if (Math.abs(x - left) < controlPointSize && Math.abs(y - top) < controlPointSize) {
  790. this.resizeHandle = 'top-left';
  791. } else if (Math.abs(x - (left + width)) < controlPointSize && Math.abs(y - top) < controlPointSize) {
  792. this.resizeHandle = 'top-right';
  793. } else if (Math.abs(x - left) < controlPointSize && Math.abs(y - (top + height)) < controlPointSize) {
  794. this.resizeHandle = 'bottom-left';
  795. } else if (Math.abs(x - (left + width)) < controlPointSize && Math.abs(y - (top + height)) < controlPointSize) {
  796. this.resizeHandle = 'bottom-right';
  797. } else {
  798. this.resizeHandle = null;
  799. }
  800. } else {
  801. this.resizeHandle = null;
  802. }
  803. }
  804. },
  805. move(e) {
  806. let touches = e.touches,
  807. touch0 = touches[0],
  808. touch1 = touches[1];
  809. if (touch1) {
  810. let x = touch1.x - touch0.x,
  811. y = touch1.y - touch0.y,
  812. fgDistance = Math.sqrt(x * x + y * y),
  813. scaleSize = 0.005 * (fgDistance - this.fgDistance),
  814. beScaleSize = this.scaleSize + scaleSize;
  815. do {
  816. if (!this.letScale) break;
  817. if (beScaleSize < this.mnScale) break;
  818. if (beScaleSize > this.mxScale) break;
  819. if (this.isin) {
  820. let imgWidth = this.useWidth * beScaleSize,
  821. imgHeight = this.useHeight * beScaleSize,
  822. rx0 = this.posWidth + this.useWidth / 2,
  823. ry0 = this.posHeight + this.useHeight / 2,
  824. l = rx0 - imgWidth / 2, t = ry0 - imgHeight / 2,
  825. r = l + imgWidth, b = t + imgHeight,
  826. left = parseInt(this.selStyle.left),
  827. top = parseInt(this.selStyle.top),
  828. width = parseInt(this.selStyle.width),
  829. height = parseInt(this.selStyle.height);
  830. if (left < l || left + width > r || top < t || top + height > b) break;
  831. this.scaleWidth = (this.useWidth - imgWidth) / 2;
  832. this.scaleHeight = (this.useHeight - imgHeight) / 2;
  833. }
  834. this.scaleSize = beScaleSize;
  835. } while (0);
  836. this.fgDistance = fgDistance;
  837. if (touch1.x !== touch0.x && this.letRotate) {
  838. x = (this.touch1.y - this.touch0.y) / (this.touch1.x - this.touch0.x);
  839. y = (touch1.y - touch0.y) / (touch1.x - touch0.x);
  840. this.rotateDeg += Math.atan((y - x) / (1 + x * y)) * 180 / Math.PI;
  841. this.touch0 = touch0;
  842. this.touch1 = touch1;
  843. }
  844. this.drawImage();
  845. } else if (this.touch0) {
  846. // 只有在允许调整大小时才处理裁剪框大小调整
  847. if (this.resizeHandle && this.letChangeSize) {
  848. const style = { ...this.selStyle };
  849. const left = parseInt(style.left);
  850. const top = parseInt(style.top);
  851. const width = parseInt(style.width);
  852. const height = parseInt(style.height);
  853. const minWidth = 50;
  854. const minHeight = 50;
  855. switch (this.resizeHandle) {
  856. case 'top-left':
  857. style.left = touch0.x + 'px';
  858. style.top = touch0.y + 'px';
  859. style.width = (left + width - touch0.x) + 'px';
  860. style.height = (top + height - touch0.y) + 'px';
  861. break;
  862. case 'top-right':
  863. style.top = touch0.y + 'px';
  864. style.width = (touch0.x - left) + 'px';
  865. style.height = (top + height - touch0.y) + 'px';
  866. break;
  867. case 'bottom-left':
  868. style.left = touch0.x + 'px';
  869. style.width = (left + width - touch0.x) + 'px';
  870. style.height = (touch0.y - top) + 'px';
  871. break;
  872. case 'bottom-right':
  873. style.width = (touch0.x - left) + 'px';
  874. style.height = (touch0.y - top) + 'px';
  875. break;
  876. }
  877. // 确保最小尺寸
  878. if (parseInt(style.width) >= minWidth && parseInt(style.height) >= minHeight) {
  879. // 确保裁剪框不超出屏幕边界
  880. if (parseInt(style.left) >= 0 &&
  881. parseInt(style.top) >= 0 &&
  882. (parseInt(style.left) + parseInt(style.width)) <= this.windowWidth &&
  883. (parseInt(style.top) + parseInt(style.height)) <= (this.windowHeight - tabHeight)) {
  884. this.selStyle = style;
  885. // 重新绘制操作层
  886. this.drawInit();
  887. }
  888. }
  889. } else {
  890. // 原有的移动图片逻辑
  891. let x = touch0.x - this.touch0.x,
  892. y = touch0.y - this.touch0.y,
  893. beX = this.posWidth + x,
  894. beY = this.posHeight + y;
  895. if (this.isin) {
  896. let imgWidth = this.useWidth * this.scaleSize,
  897. imgHeight = this.useHeight * this.scaleSize,
  898. rx0 = beX + this.useWidth / 2,
  899. ry0 = beY + this.useHeight / 2,
  900. l = rx0 - imgWidth / 2, t = ry0 - imgHeight / 2,
  901. r = l + imgWidth, b = t + imgHeight,
  902. left = parseInt(this.selStyle.left),
  903. top = parseInt(this.selStyle.top),
  904. width = parseInt(this.selStyle.width),
  905. height = parseInt(this.selStyle.height);
  906. if (!this.lckWidth && Math.abs(x) < 100) {
  907. if (left >= l && left + width <= r) {
  908. this.posWidth = beX;
  909. } else if (left < l) {
  910. this.posWidth = left - this.scaleWidth;
  911. } else if (left + width > r) {
  912. this.posWidth = left - (imgWidth - width) - this.scaleWidth;
  913. }
  914. }
  915. if (!this.lckHeight && Math.abs(y) < 100) {
  916. if (top >= t && top + height <= b) {
  917. this.posHeight = beY;
  918. } else if (top < t) {
  919. this.posHeight = top - this.scaleHeight;
  920. } else if (top + height > b) {
  921. this.posHeight = top - (imgHeight - height) - this.scaleHeight;
  922. }
  923. }
  924. } else {
  925. if (Math.abs(x) < 100 && !this.lckWidth) this.posWidth = beX;
  926. if (Math.abs(y) < 100 && !this.lckHeight) this.posHeight = beY;
  927. }
  928. this.touch0 = touch0;
  929. this.drawImage();
  930. }
  931. }
  932. },
  933. end(e) {
  934. let touches = e.touches,
  935. touch0 = touches && touches[0],
  936. touch1 = touches && touches[1];
  937. if (touch0) {
  938. this.touch0 = touch0;
  939. } else {
  940. this.touch0 = null;
  941. this.touch1 = null;
  942. this.resizeHandle = null; // 重置调整手柄
  943. }
  944. },
  945. async getImgData() {
  946. await this.initCanvasRefs();
  947. return new Promise((resolve, reject) => {
  948. let prvX = this.prvX,
  949. prvY = this.prvY,
  950. prvWidth = this.prvWidth,
  951. prvHeight = this.prvHeight;
  952. // #ifdef APP-PLUS||H5
  953. prvX *= this.pixelRatio;
  954. prvY *= this.pixelRatio;
  955. prvWidth *= this.pixelRatio;
  956. prvHeight *= this.pixelRatio;
  957. // #endif
  958. this.ctxCanvasPrv.getImageData({
  959. x: prvX,
  960. y: prvY,
  961. width: prvWidth,
  962. height: prvHeight,
  963. success(res) {
  964. resolve(res.data);
  965. },
  966. fail(err) {
  967. reject(err);
  968. }
  969. });
  970. });
  971. },
  972. async colorChange(e) {
  973. let tm_now = Date.now();
  974. if (tm_now - this.prvTm < 100) return;
  975. this.prvTm = tm_now;
  976. uni.showLoading({ mask: true });
  977. if (!this.prvImgData) {
  978. if (!(this.prvImgData = await this.getImgData().catch((res) => {
  979. uni.showToast({
  980. title: "error_read",
  981. duration: 2000,
  982. })
  983. }))) return;
  984. this.target = new Uint8ClampedArray(this.prvImgData.length);
  985. }
  986. let data = this.prvImgData,
  987. target = this.target,
  988. i = e.detail.value,
  989. r, g, b, a, h, s, l, d, p, q, t, min, max, hK, tR, tG, tB;
  990. if (i === 0) {
  991. target = data;
  992. } else {
  993. i = (i + 100) / 200;
  994. if (i < 0.005) i = 0;
  995. if (i > 0.995) i = 1;
  996. for (let n = data.length - 1; n >= 0; n -= 4) {
  997. r = data[n - 3] / 255;
  998. g = data[n - 2] / 255;
  999. b = data[n - 1] / 255;
  1000. max = Math.max(r, g, b);
  1001. min = Math.min(r, g, b);
  1002. d = max - min;
  1003. if (max === min) {
  1004. h = 0;
  1005. } else if (max === r && g >= b) {
  1006. h = 60 * ((g - b) / d);
  1007. } else if (max === r && g < b) {
  1008. h = 60 * ((g - b) / d) + 360;
  1009. } else if (max === g) {
  1010. h = 60 * ((b - r) / d) + 120;
  1011. } else if (max === b) {
  1012. h = 60 * ((r - g) / d) + 240;
  1013. }
  1014. l = (max + min) / 2;
  1015. if (l === 0 || max === min) {
  1016. s = 0;
  1017. } else if (0 < l && l <= 0.5) {
  1018. s = d / (2 * l);
  1019. } else if (l > 0.5) {
  1020. s = d / (2 - 2 * l);
  1021. }
  1022. data[n] && (a = data[n]);
  1023. if (i < 0.5) {
  1024. s = s * i / 0.5;
  1025. } else if (i > 0.5) {
  1026. s = 2 * s + 2 * i - (s * i / 0.5) - 1;
  1027. }
  1028. if (s === 0) {
  1029. r = g = b = Math.round(l * 255);
  1030. } else {
  1031. if (l < 0.5) {
  1032. q = l * (1 + s);
  1033. } else if (l >= 0.5) {
  1034. q = l + s - (l * s);
  1035. }
  1036. p = 2 * l - q;
  1037. hK = h / 360;
  1038. tR = hK + 1 / 3;
  1039. tG = hK;
  1040. tB = hK - 1 / 3;
  1041. let correctRGB = (t) => {
  1042. if (t < 0) {
  1043. return t + 1.0;
  1044. }
  1045. if (t > 1) {
  1046. return t - 1.0;
  1047. }
  1048. return t;
  1049. };
  1050. let createRGB = (t) => {
  1051. if (t < (1 / 6)) {
  1052. return p + ((q - p) * 6 * t);
  1053. } else if (t >= (1 / 6) && t < (1 / 2)) {
  1054. return q;
  1055. } else if (t >= (1 / 2) && t < (2 / 3)) {
  1056. return p + ((q - p) * 6 * ((2 / 3) - t));
  1057. }
  1058. return p;
  1059. };
  1060. r = tR = Math.round(createRGB(correctRGB(tR)) * 255);
  1061. g = tG = Math.round(createRGB(correctRGB(tG)) * 255);
  1062. b = tB = Math.round(createRGB(correctRGB(tB)) * 255);
  1063. }
  1064. a && (target[n] = a);
  1065. target[n - 3] = r;
  1066. target[n - 2] = g;
  1067. target[n - 1] = b;
  1068. }
  1069. }
  1070. let prvX = this.prvX,
  1071. prvY = this.prvY,
  1072. prvWidth = this.prvWidth,
  1073. prvHeight = this.prvHeight;
  1074. if (this.fillColor && this.fillColor !== 'transparent') {
  1075. this.ctxCanvasPrv.setFillStyle(this.fillColor);
  1076. this.ctxCanvasPrv.fillRect(prvX, prvY, prvWidth, prvHeight);
  1077. }
  1078. this.ctxCanvasPrv.draw(true);
  1079. // #ifdef APP-PLUS||H5
  1080. prvX *= this.pixelRatio;
  1081. prvY *= this.pixelRatio;
  1082. prvWidth *= this.pixelRatio;
  1083. prvHeight *= this.pixelRatio;
  1084. // #endif
  1085. this.ctxCanvasPrv.putImageData({
  1086. x: prvX,
  1087. y: prvY,
  1088. width: prvWidth,
  1089. height: prvHeight,
  1090. data: target,
  1091. fail() {
  1092. uni.showToast({
  1093. title: 'error_put',
  1094. duration: 2000
  1095. })
  1096. },
  1097. complete() {
  1098. uni.hideLoading();
  1099. }
  1100. });
  1101. },
  1102. btop(base64) {
  1103. return new Promise(function (resolve, reject) {
  1104. var arr = base64.split(','),
  1105. mime = arr[0].match(/:(.*?);/)[1],
  1106. bstr = atob(arr[1]),
  1107. n = bstr.length,
  1108. u8arr = new Uint8Array(n);
  1109. while (n--) {
  1110. u8arr[n] = bstr.charCodeAt(n);
  1111. }
  1112. return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([u8arr], { type: mime })));
  1113. });
  1114. },
  1115. }
  1116. }
  1117. </script>
  1118. <style lang="scss" scoped>
  1119. .u-cropper {
  1120. .my-canvas {
  1121. display: flex;
  1122. position: fixed !important;
  1123. background: #000000;
  1124. left: 0;
  1125. z-index: 100000;
  1126. width: 100%;
  1127. }
  1128. .my-avatar {
  1129. width: 150rpx;
  1130. height: 150rpx;
  1131. border-radius: 100%;
  1132. }
  1133. .oper-canvas {
  1134. display: flex;
  1135. position: fixed !important;
  1136. left: 0;
  1137. z-index: 100001;
  1138. width: 100%;
  1139. }
  1140. .prv-canvas {
  1141. display: flex;
  1142. position: fixed !important;
  1143. background: #000000;
  1144. left: 0;
  1145. z-index: 200000;
  1146. width: 100%;
  1147. }
  1148. .oper-wrapper {
  1149. height: 50px;
  1150. position: fixed !important;
  1151. box-sizing: border-box;
  1152. border: 1px solid #F1F1F1;
  1153. background: #ffffff;
  1154. width: 100%;
  1155. left: 0;
  1156. bottom: 0;
  1157. z-index: 100009;
  1158. flex-direction: row;
  1159. }
  1160. .oper {
  1161. display: flex;
  1162. flex-direction: column;
  1163. justify-content: center;
  1164. padding: 10rpx 20rpx;
  1165. width: 100%;
  1166. height: 100%;
  1167. box-sizing: border-box;
  1168. align-self: center;
  1169. }
  1170. .btn-wrapper {
  1171. display: flex;
  1172. flex-direction: row;
  1173. /* #ifndef H5 */
  1174. flex-grow: 1;
  1175. /* #endif */
  1176. /* #ifdef H5 */
  1177. height: 50px;
  1178. /* #endif */
  1179. justify-content: space-between;
  1180. }
  1181. .btn-wrapper view {
  1182. display: flex;
  1183. align-items: center;
  1184. justify-content: center;
  1185. font-size: 16px;
  1186. color: #333;
  1187. border: 1px solid #f1f1f1;
  1188. border-radius: 6%;
  1189. }
  1190. .hover {
  1191. background: #f1f1f1;
  1192. border-radius: 6%;
  1193. }
  1194. .clr-wrapper {
  1195. display: flex;
  1196. flex-direction: row;
  1197. flex-grow: 1;
  1198. }
  1199. .clr-wrapper view {
  1200. display: flex;
  1201. align-items: center;
  1202. justify-content: center;
  1203. font-size: 16px;
  1204. color: #333;
  1205. border: 1px solid #f1f1f1;
  1206. border-radius: 6%;
  1207. }
  1208. .my-slider {
  1209. flex-grow: 1;
  1210. }
  1211. }
  1212. </style>