u-cropper.vue 37 KB

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