我使用 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”
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
这就是该错误的原因:
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 } } }