不到30行 JS 实现一个炫酷的全景交互(1),Web前端中常见的面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

renderer.render(scene, camera) // 拍照

// 不断渲染,因为图片加载和处理需要时间,不确定何时拍照合适

requestAnimationFrame(render)

}

render()

复制代码

浏览器页面效果(记得开启手机模拟调试):

image.png

2.2 基础知识点

2.2.1 经纬度

本文是使用经纬度来操作全景,需要科普一下经纬度的知识

经纬度是经度与纬度的合称组成一个坐标系统。称为地理坐标系统,它是一种利用三度空间的球面来定义地球上的空间的球面坐标系统,能够标示地球表面上的任何一个位置。

image.png

如图所示,经度:lon,取值范围:[0,360],纬度:lat,取值范围:[-90,90];

2.2.2 经纬度转换三维坐标

球面的点{lon,lat},其中R为球体的半径,求球面的点的在ThreeJS的坐标的位置为:

image.png

解:

X = R * cos(lat)* sin( lon )

Y = R * sin( lat )

Z = R * cos( lat )*cos( lon )

注:ThreeJS中默认的坐标系是右手坐标系,X轴为左右,Y轴为上下,Z轴为前后。

2.3 生成全景的步骤

在2.1的章节中,我们已经完成了绘制一个球体,绘制全景是在其基础上要做调整:

  • 1、将相机移到球体的球心位置;

  • 2、将全景图片贴到球体的内表面;

具体步骤如下:

  • 第一步:创建一个场景(Scene)

  • 第二步:创建一个球体,并将全景图片贴到球体的内表面,放入场景中

  • 第四步:创建一个透视投影相机将camera拉到球体的中心,相机观看球体内表面

  • 第五步:通过修改经纬度来,改变相机观察的点

具体代码实现:

手把手教你制作酷炫Web全景

复制代码

效果:

Jietu20210527-172203-HD.gif

至此,我们全景制作已经完成了,(只统计js代码:共_28行_代码,我才不是标题党呢????)。

三、全景交互原理


3.1 手势交互之旋转

手势交互之旋转指单指滑动操作,这与滑动地球仪的交互是一致的。

屏幕坐标系,左上角为原点,X轴:由左向右,Y轴:由上到下, 手指在屏幕滑动会依次触发三个事件:touchstart、touchmove和touchend;event对象中记录了手指屏幕的位置

TeamTalk_IMG_2021-05-27-173217.jpg

手指在屏幕滑动过程:

  • touchstart:记录滑动起始的位置(startX,startY)

  • touchmove:记录当前位置(curX,curY)相减上一次位置的值,计算出弧长除于半径乘以factor,计算出(lon,lat)

  • touchend:暂时没有用的

其中:弧长R值的是屏幕的滑动距离

那么单指在屏幕的滑动,由P1 (clientX1,clientY1)移动到P2 (clientX1,clientY1)长度为,对应经纬度变化:

distanceX = clientX1 - clientX2   // X轴方向

distanceY = clientY1 - clientY2   // Y轴方向

// 其中R为球体半径,根据弧长公式:

lon = distanX / R

lat = distanY / R

复制代码

代码实现:

// 增加touch事件监听

let lastX, lastY       // 上次屏幕位置

let curX, curY         // 当前屏幕位置

const factor = 1 / 10  // 灵敏系数

const $wrap = document.querySelector(‘#wrap’)

// 触摸开始

$wrap.addEventListener(‘touchstart’, function (evt) {

const obj = evt.targetTouches[0] // 选择第一个触摸点

startX = lastX = obj.clientX

startY = lastY = obj.clientY

})

// 触摸中

$wrap.addEventListener(‘touchmove’, function (evt) {

evt.preventDefault()

const obj = evt.targetTouches[0]

curX = obj.clientX

curY = obj.clientY

// 参考:弧长公式

lon -= ((curX - lastX) / radius) * factor // factor为了全景旋转平稳,乘以一个灵敏系数

lat += ((curY - lastY) / radius) * factor

lastX = curX

lastY = curY

})

单指操作效果:

上面的代码已经加上全景的单指交互,但是,缺少了旋转惯性。接下来,我们加一下惯性动画:

滑动惯性实现,手指在屏幕滑动过程:

  • touchstart:记录滑动起始的位置(startX,startY, startTime)

  • touchmove:记录当前位置(curX,curY)相减上一次位置的值,乘以factor,计算出(lon,lat),【触摸跟随】

  • touchend:记录endTime,计算本次滑动过程中的平均速度,然后,每帧减去减速度d,直至速度为0或者touchstart事件被触发 【触摸结束触发惯性动画】

代码实现:

let lastX, lastY         // 上次屏幕位置

let curX, curY           // 当前屏幕位置

let startX, startY       // 开始触摸的位置,用于计算速度

let isMoving = false     // 是否停止单指操作

let speedX, speedY       // 速度

const factor = 1 / 10    // 灵敏系数,经验值

const deceleration = 0.1 // 减速度,惯性动画使用

const $wrap = document.querySelector(‘#wrap’)

// 触摸开始

$wrap.addEventListener(‘touchstart’, function (evt) {

const obj = evt.targetTouches[0] // 选择第一个触摸点

startX = lastX = obj.clientX

startY = lastY = obj.clientY

startTime = Date.now()

isMoving = true

})

// 触摸中

$wrap.addEventListener(‘touchmove’, function (evt) {

evt.preventDefault()

const obj = evt.targetTouches[0]

curX = obj.clientX

curY = obj.clientY

// 参考:弧长公式

lon -= ((curX - lastX) / radius) * factor // factor为了全景旋转平稳,乘以一个系数

lat += ((curY - lastY) / radius) * factor

lastX = curX

lastY = curY

})

// 触摸结束

$wrap.addEventListener(‘touchend’, function (evt) {

isMoving = false

var t = Date.now() - startTime

speedX = (curX - startX) / t    // X轴方向的平均速度

speedY = (curY - startY) / t    // Y轴方向的平均速度

subSpeedAnimate() // 惯性动画

})

let animateInt

// 减速度动画

function subSpeedAnimate() {

lon -= speedX * factor // X轴

lat += speedY * factor

// 减速度

speedX = subSpeed(speedX)

speedY = subSpeed(speedY)

// 速度为0或者有新的触摸事件,停止动画

if ((speedX === 0 && speedY === 0) || isMoving) {

if (animateInt) {

cancelAnimationFrame(animateInt)

animateInt = undefined

}

} else {

requestAnimationFrame(subSpeedAnimate)

}

}

// 减速度

function subSpeed(speed) {

if (speed !== 0) {

if (speed > 0) {

speed -= deceleration;

speed < 0 && (speed = 0);

} else {

speed += deceleration;

speed > 0 && (speed = 0);

}

}

return speed;

}

预览地址:azuoge.github.io/Opanorama/

3.2 手势交互之缩放

手势交互之缩放是双指操作,跟放大图片一样。

前面介绍ThreeJS,提到过相机,全景缩放也是依据相机拍照时,缩放拍摄照片内容的原理是一样的。

image.png

使用ThreeJS创建相机代码如下:

const camera = new THREE.PerspectiveCamera( fov , aspect , near , fear )

参数说明:

image.png

其中,

  • near:取默认值:0.1即可

  • fear:只要大于球体半径就可,取值为:球体半径R

  • aspect:在全景的场景已经确定了,照片的长宽比:屏幕宽度 / 屏幕高度

  • fov:视场,缩放是通过修改它的值来完成全景图片的缩放;

其实,很好理解,睁大眼睛,我们就看的视野就广,看到物体就显得小些【缩小】,反之,眯着眼,看到的视野就窄,看到物体就显得大【放大】,可以通过修改右图的 fov 的值来缩放全景图片

那么如何计算fov呢?这时候我们需要双指交互,同计算,开始触摸计算第一次双指的距离,在双指移动中不断计算双指距离,与上一次距离相除即为缩放倍数。

关键代码如下:

// 其中,(clientX1,clientY1)和(clientX2,clientY2)为双指在屏幕的当前位置

// 计算距离,简化运输不用平方计算

const distance = Math.abs(clientX1 - clientX2) + Math.abc(clientY1 - clientY)

// 计算缩放比

const scale = distance / lastDiance

// 计算新的视角

fov = camera.fov / scale

// 视角范围取值

camera.fov = Math.min(90, Math.max(fov,60)) // 90 > fov > 60 ,从参数说明中选取

// 视角需要主动更新

camera.updateProjectionMatrix()

体验地址:azuoge.github.io/Opanorama/

3.3 手机陀螺仪交互

html5事件中,deviceorientation事件,此事件是检测设备方向变化时的事件。

H5有两份坐标:

  • 地球坐标 x/y/z:在任何情况下,都是恒定方向

  • 手机平面坐标 x/y/z:相对于手机屏幕定义的方向

取值范围:

  • X轴:上下旋转Beta(X) ,取值范围:[ -180° ~ 180° ]
总结

三套“算法宝典”

28天读完349页,这份阿里面试通关手册,助我闯进字节跳动

算法刷题LeetCode中文版(为例)

人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。

最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!

由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 手机平面坐标 x/y/z:相对于手机屏幕定义的方向

取值范围:

  • X轴:上下旋转Beta(X) ,取值范围:[ -180° ~ 180° ]
总结

三套“算法宝典”

28天读完349页,这份阿里面试通关手册,助我闯进字节跳动

算法刷题LeetCode中文版(为例)

人与人存在很大的不同,我们都拥有各自的目标,在一线城市漂泊的我偶尔也会羡慕在老家踏踏实实开开心心养老的人,但是我深刻知道自己想要的是一年比一年有进步。

最后,我想说的是,无论你现在什么年龄,位于什么城市,拥有什么背景或学历,跟你比较的人永远都是你自己,所以明年的你看看与今年的你是否有差距,不想做咸鱼的人,只能用尽全力去跳跃。祝愿,明年的你会更好!

由于篇幅有限,下篇的面试技术攻克篇只能够展示出部分的面试题,详细完整版以及答案解析,有需要的可以关注

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-oAHc1vb4-1713445384264)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值