u-cropper.vue 37 KB

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