123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- import type { StaticImg } from '@/utils/static'
- import { onLoad } from '@dcloudio/uni-app'
- import { reactive, toRefs, nextTick} from 'vue'
- import { inject, onUnmounted, onMounted, type ComponentInternalInstance, ref} from 'vue'
- import { encode, decode} from 'js-base64'
- import hand from '@/assets/3-4.png'
- /**
- * 它从 `inject` 函数返回 `StaticImg` 服务
- * 图片的key就是图片名称
- * @returns { StaticImg } assets下全部的文件
- * @example
- * const staticImg = useStaticImg()
- * const logo = staticImg.logo
- */
- export const useStaticImg = (): StaticImg => inject<StaticImg>("staticImg")!
- /**
- *
- * @description 重复执行的定时器, 会在组件的unMounted阶段销毁
- *
- */
- export const useScheduler = (cb: () => void, delay: number) => {
- const stx: API.UseSchedulerFastCall = {
- timeId: null,
- start: () => {},
- pause: () => {},
- stop: () => {},
- onPlaying: () => {}
- }
- let timeId = ref<number>()
- const playing = () => {}
- const clearScheduler = () => clearInterval(timeId.value as number)
- onUnmounted(clearScheduler)
- stx.start = () => {
- timeId.value = setInterval(() => {
- cb()
- playing()
- }, delay)
- }
- stx.pause = () => clearScheduler()
- stx.onPlaying = playing
- stx.stop = () => clearScheduler()
- return stx
- }
- /**
- * @description 执行一定的定时器, 会在执行完后销毁
- * @param cb 回调函数
- * @param delay 延迟 大部分浏览器setTimeout延迟默认在四毫秒左右
- */
- export const useSchedulerOnce = (
- cb: () => void,
- delay: number = 4
- ): void => {
- const timeId = setTimeout(() => {
- cb()
- clearTimeout(timeId)
- }, delay)
- onUnmounted(() => clearTimeout(timeId))
- }
- /**
- * 它返回一个对象,其中包含导航栏的高度、导航栏内内容的高度以及导航栏的顶部位置。
- * @returns 具有 height、contentHeight 和 top 属性的对象。
- */
- export const useNavbarInfo = () => {
- const { uniPlatform, safeArea , ...systemInfo} = uni.getSystemInfoSync()
- console.log(uniPlatform, window['navigatorBarHeight'], window['statusBarHeight']);
-
- if (uniPlatform == "web") {
- const statusBarHeight = window['statusBarHeight'] ? window['statusBarHeight'] * 2 : 33
- return {
- height: 140 + statusBarHeight + "rpx",
- contentHeight: '64rpx',
- top: 38 + statusBarHeight + 'rpx',
- left: '40rpx',
- safeAreaTop: statusBarHeight + 'rpx',
- statusBarHeight: statusBarHeight
- }
-
- } else {
- const bounding = uni.getMenuButtonBoundingClientRect()
- console.log("(safeArea!.top + bounding.height):", (safeArea!.top + bounding.height));
-
- return {
- height: 104 + 'px',
- contentHeight: bounding.height + (bounding.top - systemInfo.statusBarHeight!) * 2 + "px",
- top: systemInfo.statusBarHeight! + "px",
- left: '20px',
- safeAreaTop: 0 + 'rpx',
- statusBarHeight: 0
- }
- }
- }
- /**
- * 它需要一个 id、一个回调函数和一个 vm,然后它使用具有给定 id 的元素的 boundingClientRect 调用回调函数
- * @param {string} id - 您要从中获取信息的元素的 ID
- * @param cb - 回调函数
- * @param {ComponentInternalInstance} vm - 组件实例
- */
- export const useQueryElInfo = (
- id: string,
- cb: (params: UniApp.NodeInfo | UniApp.NodeInfo[] ) => void,
- vm?: ComponentInternalInstance,
- immediate: boolean = true
- ) => {
- const queryNode = () => {
- const query = vm ? uni.createSelectorQuery().in(vm) : uni.createSelectorQuery();
- query.select(id).boundingClientRect(data => cb(data)).exec();
- }
- // onMounted(() => queryNode())
- immediate ? onMounted(() => queryNode()) : queryNode()
- }
- /**
- * 它创建一个音频实例,并返回一个具有 destroy、play 和 onPlayend 方法的对象。
- * @param {string} src - 音频文件的来源
- * @returns 具有三个属性的对象:destroy、play 和 onPlayend。
- */
- export const useAudioMange = (src?: string) => {
- const audioInstance = ref< UniApp.InnerAudioContext>()
-
- audioInstance.value = uni.createInnerAudioContext()
- if (src) {
-
- audioInstance.value.src = src
- }
- const destroy = () => audioInstance.value?.stop()
- const play = (src: string) => {
- audioInstance.value!.src = src
- console.log(src, audioInstance.value);
-
- useSchedulerOnce(() => {
- console.log('play');
- audioInstance.value?.play()
- }, 500)
- }
- audioInstance.value.autoplay = true
- audioInstance.value!.onCanplay((e) => {
- console.log('onCanplay:111', e);
- })
- audioInstance.value?.onError((e) => {
- console.log(e);
- })
- const onplayend = (cb: Function) => audioInstance.value?.onEnded(() => cb && cb())
- onUnmounted(() => {
- destroy()
- })
- const atx = {
- destroy,
- play,
- onplayend,
- }
- return atx
- }
- /**
- * 它以字符串形式返回设备平台。
- * @returns uniPlatform 作为 DEVICE.Platform 返回。
- */
- export const usePlatform = (): DEVICE.Platform => {
- const { uniPlatform } = uni.getSystemInfoSync()
- return uniPlatform as DEVICE.Platform
- }
- /**
- * 获取多设备动态适配的比率
- * @example
- * const rate = useAdaptationIpadAndPhone()
- * node.width = node.width * rate * 2 + 'rpx
- */
- export const useAdaptationIpadAndPhone = () => {
- let a = window.innerWidth / window.innerHeight > 0.5 ? 667 / ( window.innerHeight ) : 1
- const resizeHanlde = () => {
- // 获取当前窗口的宽度
- var screenWidth = window.innerWidth;
- var screenHeight = window.innerHeight;
- const rate = screenWidth / screenHeight
- if ( rate > 0.5 ) { // 大于0.6 说明是平板
- a = 667 / ( screenHeight )
- } else {
- a = 1
- }
- }
- // 监听窗口尺寸变化事件
- window.addEventListener('resize', resizeHanlde);
- return a
- }
- export const useQueryParmas = async () => {
- let state = reactive({})
-
- state = await new Promise( (resolve) => {
- onLoad(query => {
- console.log(3);
- resolve(query!)
- })
- })
-
- return {...toRefs(state)}
- }
- /**
- * 这是一个导出对象的TypeScript函数,有两个方法decode和encode,分别将Base64编码的字符串解码为JSON对象,将JSON对象编码为Base64编码的字符串。
- * @returns 正在返回名为“useBase64”的自定义挂钩。这个钩子有两个函数:`decode` 和 `encode`。 `decode` 函数将字符串作为输入并返回解析后的 JSON 对象。
- * `encode` 函数将 `API.P` 类型的对象作为输入并返回 base64 编码的字符串。
- */
- export const useBase64 = () => {
- return {
- decode: <T>(URI: string): T => JSON.parse(decode(URI)),
- encode: (records: API.P): string => encode(JSON.stringify(records))
- }
- }
- /**
- * @description 获取设备方向
- * @param cb
- */
- export const useDeviceDire = (cb: (dire: 'H' | 'V') => void) => {
- const getDire = () => {
- if (window.orientation === 180 || window.orientation === 0) {
- cb('V')
- }
- if (window.orientation === 90 || window.orientation === -90) {
- cb('H')
- }
- }
- window.addEventListener('orientationchange', getDire)
- onMounted(getDire)
- onUnmounted(() => {
- window.removeEventListener('orientationchange', () => {})
- })
- }
- /**
- * @description 题卡的放大镜功能
- */
- export const useMagnifier = async (
- canvas: HTMLCanvasElement,
- canvasId: string,
- { ansUrl, quesUrl, instance, rate }
- :
- { ansUrl: string, quesUrl: string, instance: ComponentInternalInstance, rate: number}
- ) => {
- function isIOS() {
- const userAgent = navigator.userAgent;
- return /iPhone|iPad|iPod/i.test(userAgent);
- }
- const adaptatio = useAdaptationIpadAndPhone()
- let ctx: UniApp.CanvasContext = uni.createCanvasContext(canvasId)
- const deviceWidthRate = uni.getSystemInfoSync().windowWidth / 375
- // 初始化放大镜的参数
- const magnifierSize = 150 * deviceWidthRate ; // 放大镜尺寸
- const magnification = 1.5 ; // 放大倍数
- const lineWidth = 6 * deviceWidthRate// 放大镜边框宽度
- let _moveMagnifier = (event: TouchEvent) => {}
- const createImgStr = async () => {
- ctx.drawImage(quesUrl, 0, 0, 224 * deviceWidthRate, 386 * deviceWidthRate)
- ctx.drawImage(ansUrl, 225 * deviceWidthRate , 0, 75 * deviceWidthRate, 386 * deviceWidthRate)
- ctx.save()
- ctx.draw()
- // ctx.restore();
- // 创建 Image 对象并加载图片
- await new Promise( resolve => {
- useSchedulerOnce(() => {
- resolve(true)
- }, 100)
- })
-
- return await new Promise((resolve) => {
- uni.canvasToTempFilePath({
- canvasId: canvasId,
- fileType: 'png',
- quality: 1,
- destWidth: 300 * deviceWidthRate,
- destHeight: 386 * deviceWidthRate,
- success: (res) => resolve( res.tempFilePath),
- fail: (error) => {
- console.error('canvasToTempFilePath failed: ', error);
- }
- }, instance);
- }) as string
- }
- const draw = async () => {
- const imgStr = await createImgStr()
-
- function initMagnifier () {
- // 计算放大镜区域的位置和尺寸
- const startX = 200;
- const startY = 0;
- const width = magnifierSize;
- const height = magnifierSize;
- // 绘制阴影
- ctx.shadowColor = 'black';
- ctx.shadowBlur = 20;
- ctx.shadowOffsetX = 0;
- ctx.shadowOffsetY = 0;
-
- // 绘制放大镜效果
- ctx.save();
- ctx.beginPath();
- ctx.arc(
- startX,
- startY,
- magnifierSize / 2,
- 0,
- 2 * Math.PI
- );
- ctx.strokeStyle = 'yellow';
- ctx.lineWidth = lineWidth;
- ctx.stroke();
- ctx.closePath();
- ctx.clip();
- ctx.drawImage(
- imgStr,
- startX,
- startY,
- width,
- height,
- startX - magnifierSize / 2,
- startY - magnifierSize / 2,
- magnifierSize * magnification,
- magnifierSize * magnification
- );
- ctx.draw()
- ctx.restore();
- }
-
- function moveMagnifier (event: TouchEvent) {
-
- event.preventDefault();
- event.stopPropagation();
- let mouseX = Math.floor((event.touches[0].pageX - canvas.getBoundingClientRect().left) / adaptatio)
- let mouseY = Math.floor((event.touches[0].pageY - canvas.getBoundingClientRect().top) / adaptatio)
-
-
- // 拖拽边界处理
- if (mouseX <= 0) mouseX = 0
- if (mouseX >= canvas.getBoundingClientRect().right - 26 ) mouseX = canvas.getBoundingClientRect().right - 26
- if (mouseY <= 0) mouseY = 0
- if (mouseY >= (canvas.getBoundingClientRect().bottom - 214 )) mouseY = canvas.getBoundingClientRect().bottom - 214
- // 拖拽边界处理
- // 清空 Canvas
- ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
-
- // 计算放大镜区域的位置和尺寸
- const startX = Math.floor((mouseX - magnifierSize / 2 ))
- const startY = Math.floor((mouseY - magnifierSize / 2 ))
- const width = magnifierSize;
- const height = magnifierSize;
-
- // 绘制放大镜效果
- ctx.save();
- ctx.beginPath();
- ctx.arc(mouseX, mouseY, magnifierSize / 2, 0, 2 * Math.PI);
- ctx.strokeStyle = 'yellow';
- ctx.lineWidth = lineWidth;
- ctx.stroke()
- ctx.closePath();
- ctx.clip();
- const sxCount = mouseX - (magnifierSize * magnification) / 2
- const syCount = mouseY - (magnifierSize * magnification) / 2
-
- const sx = sxCount <= 0 ? 0 : mouseX - (magnifierSize * magnification) / 2
- const sy = syCount <= 0 ? 0 : (mouseY - (magnifierSize * magnification) / 2)
- ctx.drawImage(
- imgStr,
- startX,
- startY,
- width,
- height,
- isIOS() ? sx: sxCount ,
- isIOS() ? sy : syCount,
- magnifierSize * magnification ,
- magnifierSize * magnification
- );
-
- // ctx.drawImage(
- // imgStr,
- // startX,
- // startY,
- // width,
- // height,
- // mouseX - (magnifierSize * magnification) / 2 ,
- // mouseY - (magnifierSize * magnification) / 2 ,
- // magnifierSize * magnification ,
- // magnifierSize * magnification
- // );
-
- ctx.draw()
- ctx.restore();
- }
- _moveMagnifier = moveMagnifier
- initMagnifier();
- // 监听鼠标移动事件,实现放大镜效果
- canvas.addEventListener("touchmove",moveMagnifier);
- }
- const clear = () => {
- ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
- ctx.draw()
- canvas.removeEventListener('touchmove', _moveMagnifier)
- }
- onUnmounted(() => {
- canvas.removeEventListener('touchmove', _moveMagnifier)
- })
-
- return {
- draw, clear
- }
- };
- /**
- * @description 陀螺仪 根据陀螺仪判断是否是横屏状态
- */
- export const useGyroH = async (cb: (dire: string) => void) => {
- const handleOrientation = (event: DeviceOrientationEvent) => {
- var alpha = event.alpha!; // 设备绕 Z 轴的旋转角度(0 到 360 度)
- var beta = event.beta!; // 设备绕 X 轴的旋转角度(-180 到 180 度)
- var gamma = event.gamma!; // 设备绕 Y 轴的旋转角度(-90 到 90 度)
- console.log(`x轴:${Math.floor(beta)} y轴:${ Math.floor(gamma)} z轴:${ Math.floor(alpha) }`);
-
- let dire = ''
- if (alpha < 10 ) {
- dire = 'H'
- } else {
- dire = 'V'
- }
-
- cb && cb(dire)
- }
-
- if (window.DeviceOrientationEvent) {
- window.addEventListener('deviceorientation', handleOrientation);
- // if ( uni.getSystemInfoSync().system.includes('iOS')) { }
- } else {
- console.log('设备不支持陀螺仪功能');
- }
- onUnmounted(() => {
- window.removeEventListener('deviceorientation', handleOrientation)
- })
- }
|