u-cropper.vue 37 KB

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