143 lines
4.5 KiB
JavaScript
143 lines
4.5 KiB
JavaScript
/**
|
|
* 全息镭射卡预览组合式函数
|
|
*
|
|
* 集成 WebGL HolographicEngine + 陀螺仪/触摸交互
|
|
* 与现有 useLenticularPreview / useLenticularStudioTilt 架构保持一致
|
|
*/
|
|
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
|
|
|
function clamp(v, min, max) { return Math.max(min, Math.min(max, v)) }
|
|
function lerp(a, b, t) { return a + (b - a) * t }
|
|
|
|
export function useHolographicPreview() {
|
|
const physics = reactive({
|
|
tiltSensitivity: 72,
|
|
transitionSmoothness: 66,
|
|
angleStability: 88,
|
|
sensorDeadzoneStrength: 1,
|
|
gyroSimEnabled: true,
|
|
})
|
|
const viewAngle = reactive({ x: 0, y: 0, z: 0 })
|
|
const gyroSource = ref('simulation')
|
|
const isWebGLReady = ref(false)
|
|
const hasWebGLError = ref(false)
|
|
const fps = ref(0)
|
|
|
|
let accelHandler = null
|
|
let accelSmoothed = 0
|
|
let accelBaselineReady = false
|
|
let accelBaseX = 0
|
|
let accelBaseY = 0
|
|
|
|
function simulate(x, y) {
|
|
viewAngle.x = clamp(x, -1, 1)
|
|
viewAngle.y = clamp(y != null ? y : 0, -1, 1)
|
|
viewAngle.z = Math.sqrt(viewAngle.x * viewAngle.x + viewAngle.y * viewAngle.y) * 0.5
|
|
}
|
|
|
|
function relax(factor = 0.85) {
|
|
viewAngle.x *= factor
|
|
viewAngle.y *= factor
|
|
viewAngle.z *= factor
|
|
}
|
|
|
|
function startGyro() {
|
|
gyroSource.value = 'simulation'
|
|
if (typeof DeviceOrientationEvent === 'undefined') return
|
|
|
|
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
|
|
gyroSource.value = 'deviceorientation-requesting'
|
|
DeviceOrientationEvent.requestPermission()
|
|
.then(state => { if (state === 'granted') startDeviceOrientation() })
|
|
.catch(() => { gyroSource.value = 'simulation' })
|
|
} else {
|
|
startDeviceOrientation()
|
|
}
|
|
}
|
|
|
|
function startDeviceOrientation() {
|
|
gyroSource.value = 'deviceorientation'
|
|
const handler = (e) => {
|
|
if (e.gamma == null || e.beta == null) return
|
|
const gamma = e.gamma || 0
|
|
const beta = e.beta || 0
|
|
if (!accelBaselineReady) {
|
|
accelBaseX = lerp(accelBaseX, gamma, 0.08)
|
|
accelBaseY = lerp(accelBaseY, beta, 0.08)
|
|
if (Math.abs(accelBaseX - gamma) < 0.3 && Math.abs(accelBaseY - beta) < 0.3) {
|
|
accelBaselineReady = true
|
|
}
|
|
return
|
|
}
|
|
const stab = clamp(physics.angleStability / 100, 0, 1)
|
|
const sens = physics.tiltSensitivity / 100
|
|
const k = 0.02 + (1 - stab) * 0.08
|
|
const dx = (gamma - accelBaseX) / 45 * sens
|
|
const dy = (beta - accelBaseY) / 45 * sens
|
|
accelSmoothed = lerp(accelSmoothed, dx, k)
|
|
const dead = physics.sensorDeadzoneStrength || 1
|
|
const db = 0.016 * dead
|
|
simulate(
|
|
Math.abs(accelSmoothed) < db ? 0 : clamp(accelSmoothed, -1, 1),
|
|
clamp(dy, -1, 1)
|
|
)
|
|
}
|
|
window.addEventListener('deviceorientation', handler, true)
|
|
accelHandler = handler
|
|
}
|
|
|
|
function stopGyro() {
|
|
if (accelHandler) {
|
|
window.removeEventListener('deviceorientation', accelHandler, true)
|
|
accelHandler = null
|
|
}
|
|
gyroSource.value = 'simulation'
|
|
accelBaselineReady = false
|
|
}
|
|
|
|
function onWebGLReady() { isWebGLReady.value = true; hasWebGLError.value = false }
|
|
function onWebGLError() { hasWebGLError.value = true; isWebGLReady.value = false }
|
|
function onFPSUpdate(v) { fps.value = v }
|
|
|
|
onMounted(() => {
|
|
if (physics.gyroSimEnabled) setTimeout(startGyro, 600)
|
|
})
|
|
onUnmounted(() => { stopGyro() })
|
|
|
|
return {
|
|
physics, viewAngle, gyroSource,
|
|
isWebGLReady, hasWebGLError, fps,
|
|
simulate, relax, startGyro, stopGyro,
|
|
onWebGLReady, onWebGLError, onFPSUpdate,
|
|
}
|
|
}
|
|
|
|
export function detectPerformanceTier() {
|
|
const mem = navigator.deviceMemory || 4
|
|
const cores = navigator.hardwareConcurrency || 4
|
|
if (mem <= 2 || cores <= 2) return 'low'
|
|
if (mem <= 4 || cores <= 4) return 'mid'
|
|
return 'high'
|
|
}
|
|
|
|
export const HOLO_PERFORMANCE_PRESETS = {
|
|
high: {
|
|
effectIntensity: 0.85, dispersionStrength: 1.0, diffractionScale: 0.7,
|
|
highlightSpeed: 0.8, highlightWidth: 1.0, fresnelPower: 3.5,
|
|
noiseScale: 1.0, noiseOctaves: 6,
|
|
safeZoneRadius: 0.35, safeZoneSoftness: 0.15,
|
|
},
|
|
mid: {
|
|
effectIntensity: 0.75, dispersionStrength: 0.8, diffractionScale: 0.55,
|
|
highlightSpeed: 0.7, highlightWidth: 1.1, fresnelPower: 3.0,
|
|
noiseScale: 0.8, noiseOctaves: 4,
|
|
safeZoneRadius: 0.35, safeZoneSoftness: 0.15,
|
|
},
|
|
low: {
|
|
effectIntensity: 0.6, dispersionStrength: 0.5, diffractionScale: 0.35,
|
|
highlightSpeed: 0.5, highlightWidth: 1.3, fresnelPower: 2.5,
|
|
noiseScale: 0.55, noiseOctaves: 3,
|
|
safeZoneRadius: 0.38, safeZoneSoftness: 0.18,
|
|
},
|
|
}
|