可以看到网上大部分网页实现裸眼3d效果有两种
第一种对性能要求比较大,毕竟需要页面进行3d渲染;第二种属于一种伪3d效果,图片都是平面的,并不会因为视角的切换看到另一面的东西。
通过鼠标从左往右移动,来对一张大的宫格图的不同位置进行切换,来实现不同视角的切换。
首先,访问链接下载blender插件: https://github.com/regcs/AliceLG/releases
在blender中安装插件,Edit
->Preferences
->Add-ons
->Install
安装后记得安装依赖,会有红色按钮,点击后安装依赖,然后重启blender。会在工具栏看到插件
然后,需要设置渲染图片的输出地址
设置裁切面起始和结束的位置,以及焦距平面的位置,保证你的模型在这个范围里
然后点击Render Quilt
渲染宫格图
点击保存即可拿到图片啦
可以看到他是以右上角作为最左边的一个视角图,再往下到左下角作为最右边的一个视角图。
transform-style: preserve-3d;
设置元素的子元素是位于 3D 空间中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3d image block</title>
<style>
html,
body {
width: 100%;
height: 100%;
background: #02020c;
}
.image-container {
margin-top: 20px;
display: flex;
justify-content: center;
background: #000;
}
.image-block {
width: 480px;
height: 636px;
overflow: hidden;
transform-style: preserve-3d;
border-radius: 5px;
}
.inner {
width: 100%;
height: 100%;
transform-style: preserve-3d;
}
.img {
background-size: 100% 100%;
opacity: 1;
}
</style>
</head>
<body>
<!-- ======================= 3d image block start ========================== -->
<div id="js-container" class="image-container">
<div id="js-block" class="image-block">
<div class="inner">
<div class="img" id="js-img"></div>
</div>
</div>
</div>
上面分析的宫格图顺序,可以通过getCoordinates
方法来进行宫格图定位,通过传入行列数和当前序号,来找到当前图片的位置。
/**
* 获取坐标
* @param {*} rows 行数
* @param {*} cols 列数
* @param {*} index 索引
* @returns
*/
function getCoordinates(rows, cols, index) {
const adjustedIndex = index + skipImages // 调整索引以跳过前几张图
const row = Math.floor(adjustedIndex / cols)
const col = cols - 1 - (adjustedIndex % cols)
return { rowNum: row, colNum: col }
}
监听mousemove
事件,计算出鼠标移动的速度
/**
* 鼠标移动
*/
block.addEventListener('mousemove', function (e) {
const rect = block.getBoundingClientRect()
const currentMouseX = e.clientX - rect.left
const currentTime = Date.now()
// 计算鼠标速度
if (lastTime !== 0) {
const deltaX = currentMouseX - lastMouseX
const deltaTime = currentTime - lastTime
mouseSpeed = deltaX / deltaTime // 速度 = 距离 / 时间
// 限制最大速度
if (mouseSpeed > maxSpeed) {
mouseSpeed = maxSpeed
} else if (mouseSpeed < -maxSpeed) {
mouseSpeed = -maxSpeed
}
}
lastMouseX = currentMouseX
lastTime = currentTime
targetX = currentMouseX
})
然后通过requestAnimationFrame
来进行每帧的图片切换,并添加惯性,让视角切换更流畅。通过添加div的旋转角度,让3d效果更佳明显。
/**
* 动画, 每帧执行
*/
function animate() {
// 使用缓动函数平滑速度变化
velocity += (targetX - currentX) * 0.05 * Math.abs(mouseSpeed)
velocity *= friction
currentX += velocity
// 计算当前索引
const blockWidth = block.offsetWidth
const unitWidth = Math.round(blockWidth / totalViews)
let theIndex = Math.round(currentX / unitWidth)
// 确保索引在有效范围内
if (theIndex < 0) {
theIndex = 0
} else if (theIndex > totalViews - 1) {
theIndex = totalViews - 1
}
const { rowNum, colNum } = getCoordinates(row, col, theIndex)
// 设置旋转角度和视点图
const container = document.getElementById('js-container')
const rotateY = (currentX - blockWidth / 2) / blockWidth * 30
block.style.transform = `perspective(${container.offsetHeight ? container.offsetHeight * 2 : 2400}px) rotateY(${rotateY}deg)`
img.style.transform = `translate(calc(-100% / ${col} * ${colNum}),calc(-100% / ${row} * ${rowNum}))`
// 继续动画
animationFrameId = requestAnimationFrame(animate)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3d image block</title>
<style>
html,
body {
width: 100%;
height: 100%;
background: #02020c;
}
.image-container {
margin-top: 20px;
display: flex;
justify-content: center;
background: #000;
}
.image-block {
width: 480px;
height: 636px;
overflow: hidden;
transform-style: preserve-3d;
border-radius: 5px;
}
.inner {
width: 100%;
height: 100%;
transform-style: preserve-3d;
}
.img {
background-size: 100% 100%;
opacity: 1;
}
</style>
</head>
<body>
<!-- ======================= 3d image block start ========================== -->
<div id="js-container" class="image-container">
<div id="js-block" class="image-block">
<div class="inner">
<div class="img" id="js-img"></div>
</div>
</div>
</div>
<script>
const img = document.getElementById('js-img')
const block = document.getElementById('js-block')
let row = 6 // 行数
let col = 11 // 列数
let skipImages = 0 // 需要跳过的图像数量
let totalViews = row * col - skipImages // 总视图数
let initialIndex = Math.floor(totalViews / 2) // 计算中间索引
let { rowNum: initialRow, colNum: initialCol } = getCoordinates(row, col, initialIndex)
let currentX = 0 // 当前位置
let targetX = 0 // 目标位置
let velocity = 0 // 速度
let friction = 0.85 // 摩擦系数
let lastMouseX = 0 // 上一次鼠标位置
let lastTime = 0 // 上一次时间
let mouseSpeed = 0 // 鼠标速度
const maxSpeed = 40 // 增大最大速度限制
let lastVelocity = 0 // 添加变量记录最后的速度
let animationFrameId // requestAnimationFrame 的 ID
// 设置显示的图片
img.style.width = `calc(100% * ${col})`
img.style.height = `calc(100% * ${row})`
img.style.backgroundImage = `url(./images/test.png)`
img.style.transform = `translate(calc(-100% / ${col} * ${initialCol}),calc(-100% / ${row} * ${initialRow}))`
/**
* 鼠标移动
*/
block.addEventListener('mousemove', function (e) {
const rect = block.getBoundingClientRect()
const currentMouseX = e.clientX - rect.left
const currentTime = Date.now()
// 计算鼠标速度
if (lastTime !== 0) {
const deltaX = currentMouseX - lastMouseX
const deltaTime = currentTime - lastTime
mouseSpeed = deltaX / deltaTime // 速度 = 距离 / 时间
// 限制最大速度
if (mouseSpeed > maxSpeed) {
mouseSpeed = maxSpeed
} else if (mouseSpeed < -maxSpeed) {
mouseSpeed = -maxSpeed
}
}
lastMouseX = currentMouseX
lastTime = currentTime
targetX = currentMouseX
})
// 调用初始设置函数
initialSetup()
// 启动动画
animate()
/**
* 初始化时设置中间图片和角度为0
*/
function initialSetup() {
img.style.transform = `translate(calc(-100% / ${col} * ${initialCol}),calc(-100% / ${row} * ${initialRow}))`
block.style.transform = 'perspective(2400px) rotateY(0deg)'
}
/**
* 动画, 每帧执行
*/
function animate() {
// 使用缓动函数平滑速度变化
velocity += (targetX - currentX) * 0.05 * Math.abs(mouseSpeed)
velocity *= friction
currentX += velocity
// 计算当前索引
const blockWidth = block.offsetWidth
const unitWidth = Math.round(blockWidth / totalViews)
let theIndex = Math.round(currentX / unitWidth)
// 确保索引在有效范围内
if (theIndex < 0) {
theIndex = 0
} else if (theIndex > totalViews - 1) {
theIndex = totalViews - 1
}
const { rowNum, colNum } = getCoordinates(row, col, theIndex)
// 设置旋转角度和视点图
const container = document.getElementById('js-container')
const rotateY = (currentX - blockWidth / 2) / blockWidth * 30
block.style.transform = `perspective(${container.offsetHeight ? container.offsetHeight * 2 : 2400}px) rotateY(${rotateY}deg)`
img.style.transform = `translate(calc(-100% / ${col} * ${colNum}),calc(-100% / ${row} * ${rowNum}))`
// 继续动画
animationFrameId = requestAnimationFrame(animate)
}
/**
* 获取坐标
* @param {*} rows 行数
* @param {*} cols 列数
* @param {*} index 索引
* @returns
*/
function getCoordinates(rows, cols, index) {
const adjustedIndex = index + skipImages // 调整索引以跳过前几张图
const row = Math.floor(adjustedIndex / cols)
const col = cols - 1 - (adjustedIndex % cols)
return { rowNum: row, colNum: col }
}
</script>
</body>
</html>
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。