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

viewfinder.vue 11KB

11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
1 jaar geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
11 maanden geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <template>
  2. <view style="width: 100%;height: 100vh;position: fixed;left: 0;top:0;z-index: 111111;">
  3. <view ref="targetView" class="container viewfinder">
  4. <view v-if="phoneType == 1"
  5. style="display: flex;align-items: center;justify-content: flex-end;width: 100%;height: 100%;">
  6. <image :src="fileURL + 'image/index/etc_bd_ocr_id_card_locator_front.png'"
  7. style="height: 220rpx;margin-right: 30rpx;" mode="heightFix"></image>
  8. </view>
  9. <view v-if="phoneType == 2" style="margin: 30rpx 0 0 30rpx;">
  10. <image :src="fileURL + 'image/index/etc_bd_ocr_id_card_locator_back.png'" style="height: 160rpx;"
  11. mode="heightFix">
  12. </image>
  13. </view>
  14. <view v-if="phoneType == 3"
  15. style="margin: -30rpx 0 0 30rpx;display: flex;height: 100%;align-self: flex-end;flex-direction: column;justify-content: flex-end;">
  16. <view style="border: 1px solid #fff;height: 160rpx;width: 160rpx;">
  17. </view>
  18. </view>
  19. <view v-if="phoneType == 4"
  20. style="margin: -30rpx 30rpx 0 0;display: flex;height: 100%;justify-content: flex-end;align-items: flex-end;">
  21. <view style="border: 1px solid #fff;height: 60rpx;width: 300rpx;">
  22. </view>
  23. </view>
  24. <canvas canvas-id="canvasbg" id="canvasbg" style="width: 100%;height: 100%;"></canvas>
  25. </view>
  26. <!-- camera -->
  27. <camera v-if="startPhoto" id="camera" style="height: 70vh;width: 100vh;background-color: black;width: 100%;"
  28. mode="normal" :device-position="cameraPosition" :flash="flash" @stop="cameraStop" @error="cameraError" />
  29. <view v-if="!startPhoto" style="height: 70vh;background-color: black;">
  30. <canvas canvas-id="canvasId" id="canvasId" style="height: 100vh;width: 100vh;"
  31. @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd"
  32. :style="{ transform: `translate(${offsetX}px, ${offsetY}px) rotate(${rotateDegree}deg) scale(${scale})`, transition: 'transform ' + transitionDuration + 's' }"></canvas>
  33. </view>
  34. <view style="background-color: #A5A5A5;top: 60%;width: 100%;height: 40%;position: absolute;">
  35. <view style="display: flex;flex-direction: row;justify-content: center;align-items: center;height: 100%;">
  36. <view style="flex: 1;"></view>
  37. <image @click="camera" :src="fileURL + 'image/index/etc_bd_ocr_cancel.png'"
  38. style="width: 50rpx;height: 50rpx;"></image>
  39. <view style="flex: 1;"></view>
  40. <image @click="takePhoto" :src="fileURL + 'image/index/etc_bd_ocr_take_photo_normal.png'"
  41. :style="startPhoto ? 'width: 140rpx;height: 140rpx;' : 'width: 80rpx;height: 80rpx;'"></image>
  42. <view style="flex: 1;"></view>
  43. <image v-if="false" @click="success" :src="fileURL + 'image/index/etc_bd_ocr_confirm.png'"
  44. style="width: 50rpx;height: 50rpx;"></image>
  45. <view style="flex: 1;"></view>
  46. </view>
  47. </view>
  48. </view>
  49. </template>
  50. <script setup lang="ts">
  51. import {
  52. ref,
  53. getCurrentInstance,
  54. onMounted,
  55. nextTick
  56. } from 'vue'
  57. import {
  58. fileURL
  59. } from "@/datas/fileURL.js";
  60. let prop = defineProps({
  61. phoneType: {
  62. type: Number,
  63. default: function () {
  64. return 3 //1 身份证正面 2 身份证反面 3 行驶证正面 4 行驶证反面
  65. }
  66. },
  67. showStartPhoto: {
  68. type: Boolean,
  69. default: function () {
  70. return true
  71. }
  72. },
  73. images: {
  74. type: String
  75. }
  76. });
  77. console.log('输出内容', prop.phoneType)
  78. const emit = defineEmits<{
  79. (e : "confirmReturn", content : any) : void;
  80. (e : "failReturn", content : any) : void;
  81. (e : "camera") : void;
  82. }>();
  83. let cameraPosition = 'back' // 'back'为后置摄像头,'front'为前置摄像头
  84. let flash = 'off' // 'off'为关闭闪光灯,'on'为打开闪光灯
  85. let scaleStep = 0.5 //每次缩放的最大增量
  86. let srcImg = ref(prop.images)
  87. let rotateDegree = ref(0)
  88. let touchStartDistance = ref(0)
  89. let touchMoveDistance = ref(0)
  90. let scale = ref(1)
  91. let transitionDuration = ref(0)
  92. let startPhoto = ref(prop.showStartPhoto)
  93. const startX = ref(0);
  94. const startY = ref(0);
  95. const offsetX = ref(0);
  96. const offsetY = ref(0);
  97. const dragging = ref(false);
  98. const canvasId = 'canvasId';
  99. const targetView = ref();
  100. const currentInstance = ref();
  101. onMounted(() => {
  102. currentInstance.value = getCurrentInstance()
  103. if (!prop.showStartPhoto) {
  104. emit('confirmReturn', { tempImagePath: prop.images })
  105. }
  106. })
  107. const handleTouchStart = (event : TouchEvent) => {
  108. console.log('手柄触摸启动', '==1')
  109. if (event.touches.length === 1) {
  110. startX.value = event.changedTouches[0].pageX - offsetX.value;
  111. startY.value = event.changedTouches[0].pageY - offsetY.value;
  112. dragging.value = true;
  113. transitionDuration.value = 0.15
  114. } else if (event.touches.length === 2) {
  115. touchStartDistance.value = getTouchDistance(event.touches);
  116. transitionDuration.value = 0.5
  117. }
  118. };
  119. const handleTouchMove = (event : TouchEvent) => {
  120. console.log('手柄触摸启动', '==2')
  121. if (event.touches.length === 2) {
  122. touchMoveDistance.value = getTouchDistance(event.touches);
  123. updateScale();
  124. } else {
  125. if (dragging.value) {
  126. offsetX.value = event.changedTouches[0].pageX - startX.value;
  127. offsetY.value = event.changedTouches[0].pageY - startY.value;
  128. }
  129. }
  130. };
  131. function getTouchDistance(touches) {
  132. const x = touches[0].clientX - touches[1].clientX;
  133. const y = touches[0].clientY - touches[1].clientY;
  134. return Math.sqrt(x * x + y * y);
  135. }
  136. const handleTouchEnd = () => {
  137. console.log('手柄触摸启动', '==3')
  138. touchStartDistance.value = 0;
  139. touchMoveDistance.value = 0;
  140. dragging.value = false;
  141. };
  142. function updateScale() {
  143. // 计算缩放的增量
  144. const scales = (touchMoveDistance.value - touchStartDistance.value) * scaleStep;
  145. scale.value = Math.max(0.5, Math.min(scale.value + scales, 1)); // 设置缩放的范围,这里设置最小为1,最大为3
  146. }
  147. function takePhoto() {
  148. uni.authorize({
  149. scope: 'scope.camera',
  150. complete(res) {
  151. console.log("res===",res)
  152. // 打开了拍照权限
  153. if(res.errMsg=="authorize:ok"){
  154. console.log('输出内容', rotateDegree.value)
  155. if (prop.showStartPhoto) {
  156. let cameraContext = null
  157. // #ifdef MP-WEIXIN
  158. cameraContext = uni.createCameraContext();
  159. // #endif
  160. // #ifdef MP-ALIPAY
  161. cameraContext = uni.createCameraContext('camera');
  162. // #endif
  163. // 调用拍照方法
  164. cameraContext.takePhoto({
  165. quality: 'normal',
  166. success: (res : any) => {
  167. // 在这里处理拍照成功后的逻辑,res.tempImagePath 为拍照的图片路径
  168. // srcImg.value = res.tempImagePath
  169. startPhoto.value = false
  170. cameraContext.stopRecord();
  171. emit('confirmReturn', res)
  172. // getViewPosition(res.tempImagePath)
  173. return
  174. // console.log('拍照成功:', res.tempImagePath);
  175. },
  176. fail: (error) => {
  177. console.error('拍照失败:', error);
  178. emit('failReturn', error);
  179. },
  180. });
  181. } else {
  182. rotateDegree.value += 90
  183. }
  184. }else{
  185. uni.showModal({
  186. title: '提示',
  187. content: '请打开摄像头权限',
  188. success: function (res) {
  189. if (res.confirm) {
  190. uni.openSetting({
  191. success(res) {
  192. console.log("2222")
  193. console.log(res.authSetting)
  194. }
  195. });
  196. } else if (res.cancel) {
  197. console.log('用户点击取消');
  198. }
  199. }
  200. });
  201. }
  202. }
  203. })
  204. // console.log('输出内容', '===123')
  205. }
  206. const getViewPosition = (src : any) => {
  207. const context = uni.createCanvasContext(canvasId, currentInstance.value)
  208. const query = uni.createSelectorQuery().in(currentInstance.value)
  209. // query.in(this)
  210. query.select('.container').boundingClientRect((rect : any) => {
  211. if (rect) {
  212. uni.getSystemInfo({
  213. success: (res) => {
  214. console.log(res, '===============', res.windowHeight, res.windowWidth)
  215. // 获取图片信息
  216. uni.getImageInfo({
  217. src: src,
  218. success: (imgS) => {
  219. //((res.windowHeight - res.statusBarHeight) * 0.33)
  220. console.log('====', rect, '输出内容======', imgS.width, imgS.height) //140 1.8(res.windowHeight * 0.23)
  221. // canvas.drawImage(img, 起始 x 坐标, 起始 y 坐标, 宽度, 高度, 绘制到 Canvas 的起始 x 坐标, 绘制到 Canvas 的起始 y 坐标, 裁剪后的宽度, 裁剪后的高度);
  222. context.drawImage(src, imgS.width * 0.1, imgS.height * 0.3, rect.width * 2.2, rect.height * 2.2, rect.left, rect.top, rect.width, rect.height);
  223. context.draw(false, () => {
  224. uni.canvasToTempFilePath({
  225. canvasId: canvasId,
  226. x: rect.left,
  227. y: rect.top,
  228. width: rect.width,
  229. height: rect.height,
  230. destWidth: rect.width,
  231. destHeight: rect.height,
  232. success(res : any) {
  233. // res.tempFilePath 是转换后的图片路径
  234. res.tempImagePath = res.tempFilePath
  235. emit('confirmReturn', res)
  236. },
  237. fail(err) {
  238. console.error('canvasToTempFilePath failed', err);
  239. }
  240. }, currentInstance.value)
  241. });
  242. },
  243. fail: (error) => {
  244. console.error('获取图片信息失败', error);
  245. },
  246. });
  247. }
  248. })
  249. //把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径。在自定义组件下,第二个参数传入自定义组件实例,以操作组件内 <canvas> 组件。
  250. console.log('视图的位置信息:', rect);
  251. console.log('视图的 x 坐标:', rect.left);
  252. console.log('视图的 y 坐标:', rect.top);
  253. console.log('视图的宽度:', rect.width);
  254. console.log('视图的高度:', rect.height);
  255. }
  256. }).exec();
  257. };
  258. //成功
  259. function success() {
  260. }
  261. //获取节点信息
  262. function boundingClientRect(fun : any) {
  263. const query = uni.createSelectorQuery().in(currentInstance.value)
  264. query.select('.container').boundingClientRect((rect : any) => {
  265. if (rect) {
  266. //把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径。在自定义组件下,第二个参数传入自定义组件实例,以操作组件内 <canvas> 组件。
  267. console.log('视图的位置信息:', rect);
  268. console.log('视图的 x 坐标:', rect.left);
  269. console.log('视图的 y 坐标:', rect.top);
  270. console.log('视图的宽度:', rect.width);
  271. console.log('视图的高度:', rect.height);
  272. fun(rect)
  273. }
  274. }).exec();
  275. }
  276. //重新拍摄
  277. function camera() {
  278. emit('camera')
  279. }
  280. function cameraStop() {
  281. // 相机停止时的回调
  282. console.log('相机已停止');
  283. }
  284. function cameraError(e) {
  285. // 相机出错时的回调
  286. console.error('相机错误:', e.detail.errMsg);
  287. }
  288. </script>
  289. <style>
  290. .viewfinder {
  291. pointer-events: none;
  292. position: absolute;
  293. top: 180px;
  294. left: 40px;
  295. width: 80%;
  296. height: 200px;
  297. border: 2px solid #fff;
  298. box-sizing: border-box;
  299. border-radius: 16rpx;
  300. z-index: 2;
  301. }
  302. </style>