我使用 Vue 3 Composition api 和 Typescript 来使用 HammerJS 包实现捏合缩放。
我正在尝试遵循 CodePen 用 JavaScript 编写的工作示例:https://codepen.io/bakho/details/GBzvbB 在 Vue 中实现。
但是,当我尝试使其在我的 Vue 应用程序中运行时遇到了一些问题,我不确定如何解决它。
以下错误:
// Object is possibly 'null'. imageContainer.value.offsetWidth; // Object is possibly 'null'. imageContainer.value.appendChild(displayImage); // Object is possibly 'null'. imageContainer.value.addEventList ener...
这是完整的源代码:
<template> <h1>Image Zoom</h1> <div class="imageContainer" ref="imageContainer"></div> </template> <script lang="ts"> import Hammer from "hammerjs"; import { defineComponent } from "vue"; import { ref } from 'vue'; export default defineComponent({ setup() { const imageUrl = "https://source.unsplash.com/random"; const imageContainer = ref(null) let minScale = 1; let maxScale = 4; let imageWidth : any; let imageHeight : any; let containerWidth : any; let containerHeight : any; let displayImageX = 0; let displayImageY = 0; let displayImageScale = 1; let displayDefaultWidth : any; let displayDefaultHeight let rangeX = 0; let rangeMaxX = 0; let rangeMinX = 0; let rangeY = 0; let rangeMaxY = 0; let rangeMinY = 0; // let displayImageRangeY = 0; let displayImageCurrentX = 0; let displayImageCurrentY = 0; let displayImageCurrentScale = 1; function resizeContainer() { containerWidth = imageContainer.value.offsetWidth; containerHeight = imageContainer.value.offsetHeight; if (displayDefaultWidth !== undefined && displayDefaultHeight !== undefined) { displayDefaultWidth = displayImage.offsetWidth; displayDefaultHeight = displayImage.offsetHeight; updateRange(); displayImageCurrentX = clamp(displayImageX, rangeMinX, rangeMaxX); displayImageCurrentY = clamp(displayImageY, rangeMinY, rangeMaxY); updateDisplayImage( displayImageCurrentX, displayImageCurrentY, displayImageCurrentScale ); } } resizeContainer(); function clamp(value, min, max) { return Math.min(Math.max(min, value), max); } function clampScale(newScale) { return clamp(newScale, minScale, maxScale); } const displayImage = new Image(); displayImage.src = imageUrl; displayImage.onload = function(){ imageWidth = displayImage.width; imageHeight = displayImage.height; imageContainer.value.appendChild(displayImage); displayImage.addEventListe ner('mousedown', e => e.preventDefault(), false); displayDefaultWidth = displayImage.offsetWidth; displayDefaultHeight = displayImage.offsetHeight; rangeX = Math.max(0, displayDefaultWidth - containerWidth); rangeY = Math.max(0, displayDefaultHeight - containerHeight); } imageContainer.value.addEventLis tener('wheel', e => { displayImageScale = displayImageCurrentScale = clampScale(displayImageScale + (e.wheelDelta / 800)); updateRange(); displayImageCurrentX = clamp(displayImageCurrentX, rangeMinX, rangeMaxX) displayImageCurrentY = clamp(displayImageCurrentY, rangeMinY, rangeMaxY) updateDisplayImage(displayImageCurrentX, displayImageCurrentY, displayImageScale); }, false); function updateDisplayImage(x, y, scale) { const transform = 'translateX(' + x + 'px) translateY(' + y + 'px) translateZ(0px) scale(' + scale + ',' + scale + ')'; displayImage.style.transform = transform; displayImage.style.webkitTransform = transform; displayImage.style.transform = transform; } function updateRange() { rangeX = Math.max(0, Math.round(displayDefaultWidth * displayImageCurrentScale) - containerWidth); rangeY = Math.max(0, Math.round(displayDefaultHeight * displayImageCurrentScale) - containerHeight); rangeMaxX = Math.round(rangeX / 2); rangeMinX = 0 - rangeMaxX; rangeMaxY = Math.round(rangeY / 2); rangeMinY = 0 - rangeMaxY; } const hammertime = new Hammer(imageContainer); hammertime.get('pinch').set({ enable: true }); hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL }); hammertime.on('pan', ev => { displayImageCurrentX = clamp(displayImageX + ev.deltaX, rangeMinX, rangeMaxX); displayImageCurrentY = clamp(displayImageY + ev.deltaY, rangeMinY, rangeMaxY); updateDisplayImage(displayImageCurrentX, displayImageCurrentY, displayImageScale); }); hammertime.on('pinch pinchmove', ev => { displayImageCurrentScale = clampScale(ev.scale * displayImageScale); updateRange(); displayImageCurrentX = clamp(displayImageX + ev.deltaX, rangeMinX, rangeMaxX); displayImageCurrentY = clamp(displayImageY + ev.deltaY, rangeMinY, rangeMaxY); updateDisplayImage(displayImageCurrentX, displayImageCurrentY, displayImageCurrentScale); }); hammertime.on('panend pancancel pinchend pinchcancel', () => { displayImageScale = displayImageCurrentScale; displayImageX = displayImageCurrentX; displayImageY = displayImageCurrentY; }); return {}; }, }); </script> <style> .imageContainer { width: 96%; height: 96%; max-width: 800px; max-height: 600px; position: absolute; overflow: hidden; top: 0; right: 0; bottom: 0; left: 0; margin: auto; background: #2b2b2c; display: flex; flex-direction: column; align-items: center; justify-content: center; } .imageContainer > img { display: block; max-width: 100%; max-height: 100%; cursor: move; touch-action: none; } </style>
任何人都可以告诉我出了什么问题以及为什么会导致此 Object 可能为“null”
P粉7528260082023-11-09 00:11:04
这就是该错误的原因:
const imageContainer = ref(null)
-> 您将值设置为 null
并且 TypeScript 警告您对该对象的属性的任何访问都可能引发错误,因为初始值为 null
并且因为您尝试在模板中使用 ref
该元素可以存在,也可以不必存在。
const imageContainer = document.querySelector('.imageContainer')
-> 您正在查询存在或不存在的 HTML 元素,这意味着您还可以获取 null
作为值,并且 TypeScript 再次警告您对该对象的属性的任何访问都可能引发错误
解决方案:
setup
函数返回 setup
变量,以便 Vue 将其与模板中的 ref
绑定,并且可以在 onMounted
函数内部访问该变量,因为它是 ref
,这意味着它尚未安装到 DOM 中。 setup
在 Vue.js 组件生命周期中的 created
和 mounted
挂钩之前调用,并且您无权访问其中的任何 DOM 内容。import { ref, onMounted } from 'vue' export default { setup() { const imageContainer = ref(null) onMounted(() => { // here you access imageContainer variable }) return { imageContainer } } }