feat: 主页文案样式修改,细胞结构项目增加交互点.

This commit is contained in:
dragonir 2022-01-07 10:42:03 +08:00
parent c144181f3d
commit 8899bc7cb3
14 changed files with 144 additions and 37 deletions

View File

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>3D</title>
<title>dragonir's work list</title>
</head>
<body style="background: #212121;">
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -0,0 +1,60 @@
import * as THREE from "three";
// 创建文字
export const makeTextSprite = (text, color, parameters) => {
if (parameters === undefined) parameters = {};
var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Arial";
var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 32;
var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 4;
var borderColor = parameters.hasOwnProperty("borderColor") ? parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = fontsize + "px " + fontface;
var metrics = context.measureText(text);
var textWidth = metrics.width;
context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")";
context.lineWidth = borderThickness;
context.fillStyle = color ? color : "#ffffff";
context.fillText(text, borderThickness, fontsize + borderThickness);
context.font = 48 + "px " + fontface;
context.width = textWidth;
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({ map: texture });
var sprite = new THREE.Sprite(spriteMaterial);
return sprite;
}
// 创建圆形文字
export const makeCycleTextSprite = (text, W = 100, H = 100, borderWidth = 6, borderColor = 'white', color = 'black', textColor = 'white') => {
var canvas = document.createElement('canvas');
canvas.width = W;
canvas.height = H;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc((W + borderWidth) / 2, (H + borderWidth) / 2, 40, 0, Math.PI * 2, true);
ctx.closePath();
// 填充背景颜色
ctx.fillStyle = color;
ctx.fill();
// 填充边框颜色
ctx.lineWidth = borderWidth;
ctx.lineCap = 'round';
ctx.strokeStyle = borderColor;
ctx.stroke();
// 填充文字颜色
ctx.font = "64px Arial";
ctx.fillStyle = textColor;
ctx.textAlign = "center";
var metrics = ctx.measureText(text);
ctx.fillText(text, (W + borderWidth) / 2, (H + borderWidth * 2) / 2 + metrics.fontBoundingBoxDescent + metrics.actualBoundingBoxDescent * 2);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({
map: texture,
transparent: true,
opacity: .8,
});
var sprite = new THREE.Sprite(spriteMaterial);
return sprite;
}

View File

@ -5,12 +5,28 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import plantCellModel from './models/plant_cell.glb';
import AnimalCellModel from './models/animal_cell.glb';
import Stats from "three/examples/jsm/libs/stats.module";
import { makeCycleTextSprite } from '../../assets/utils/common';
import './index.css';
export default class Cell extends React.Component {
constructor(props) {
super(props);
this.plantCell = null;
this.plantGroup = new THREE.Group;
this.plantPoint = [
{ key: '1', value: '细胞壁', location: { x: 0, y: 4.2, z: -1 }},
{ key: '2', value: '细胞壁', location: { x: -7, y: 4, z: -1.6 }},
{ key: '3', value: '细胞壁', location: { x: 5, y: 10, z: -10 }},
{ key: '4', value: '细胞壁', location: { x: 0, y: 16, z: 10 }},
{ key: '5', value: '细胞壁', location: { x: 0, y: 10, z: 0 }},
{ key: '6', value: '细胞壁', location: { x: 3, y: 10, z: 2 }},
{ key: '7', value: '细胞壁', location: { x: 0, y: 15, z: 0 }},
{ key: '8', value: '细胞壁', location: { x: 8, y: 8, z: 8 }},
]
this.animalCell = null;
this.animalGroup = new THREE.Group;
}
state = {
@ -25,7 +41,7 @@ export default class Cell extends React.Component {
initThree = () => {
var container, controls, stats;
var camera, scene, renderer, light, earthMeshes = [];
var camera, scene, renderer, light, interactableMeshes = [];
var _this = this;
init();
animate();
@ -44,6 +60,10 @@ export default class Cell extends React.Component {
camera.position.set(0, 16, 18);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// threejs中采用的是右手坐标系红线是X轴绿线是Y轴蓝线是Z轴
var axes = new THREE.AxisHelper(30);
scene.add(axes);
const cubeGeometry = new THREE.BoxGeometry(0.001, 0.001, 0.001);
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xdc161a });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
@ -51,14 +71,7 @@ export default class Cell extends React.Component {
light = new THREE.DirectionalLight(0x03c03c, 1);
light.intensity = 1.2;
light.position.set(-5, 10, 3);
light.castShadow = true;
light.target = cube;
light.shadow.mapSize.width = 512 * 12;
light.shadow.mapSize.height = 512 * 12;
light.shadow.camera.top = 10;
light.shadow.camera.bottom = - 5;
light.shadow.camera.left = - 5;
light.shadow.camera.right = 10;
scene.add(light);
var ambientLight = new THREE.AmbientLight(0xffffff);
@ -85,9 +98,6 @@ export default class Cell extends React.Component {
loader.load(plantCellModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
earthMeshes.push(child)
child.castShadow = true;
child.receiveShadow = true;
child.material.metalness = 1;
child.material.roughness = 0;
}
@ -95,15 +105,23 @@ export default class Cell extends React.Component {
mesh.scene.position.set(0, 0, 0);
mesh.scene.scale.set(40, 40, 40);
_this.plantCell = mesh.scene;
scene.add(mesh.scene);
_this.plantGroup.add(mesh.scene);
_this.plantPoint.map(item => {
let point = makeCycleTextSprite(item.key);
point.name = item.value;
point.scale.set(1, 1, 1);
point.position.set(item.location.x, item.location.y, item.location.z);
_this.plantGroup.add(point);
interactableMeshes.push(point);
});
scene.add(_this.plantGroup);
});
// 动物细胞
loader.load(AnimalCellModel, function (mesh) {
mesh.scene.traverse(function (child) {
if (child.isMesh) {
earthMeshes.push(child)
child.castShadow = true;
child.receiveShadow = true;
child.material.metalness = 1;
child.material.roughness = 0;
}
@ -111,6 +129,7 @@ export default class Cell extends React.Component {
mesh.scene.position.set(0, -8, 0);
mesh.scene.scale.set(120, 120, 120);
_this.animalCell = mesh.scene;
_this.animalGroup.add(mesh.scene);
});
controls = new OrbitControls(camera, renderer.domElement);
@ -131,11 +150,39 @@ export default class Cell extends React.Component {
stats && stats.update();
controls && controls.update();
}
stats = new Stats();
document.documentElement.appendChild(stats.dom);
// 增加点击事件声明raycaster和mouse变量
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector3();
function onMouseClick(event) {
// 通过鼠标点击的位置计算出raycaster所需要的点的位置以屏幕中心为原点值的范围为-1到1.
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
// 通过鼠标点的位置和当前相机的矩阵计算出raycaster
// 屏幕坐标转标准设备坐标
const x1 = ( event.clientX / window.innerWidth ) * 2 - 1;
const y1 = -( event.clientX / window.innerHeight ) * 2 + 1;
//标准设备坐标(z=0.5这个值比较靠经验)
const stdVector = new THREE.Vector3(x1, y1, 0.5);
//世界坐标
const worldVector = stdVector.unproject(camera);
console.log(worldVector);
raycaster.setFromCamera(mouse, camera);
// 获取raycaster直线和所有模型相交的数组集合
var intersects = raycaster.intersectObjects(interactableMeshes);
if (intersects.length > 0) {
console.log(intersects[0].object);
}
}
window.addEventListener('click', onMouseClick, false);
}
handleToggle = () => {
this.scene.remove(this.state.showAnimal ? this.animalCell : this.plantCell);
this.scene.add(this.state.showAnimal ? this.plantCell : this.animalCell);
this.scene.remove(this.state.showAnimal ? this.animalGroup : this.plantGroup);
this.scene.add(this.state.showAnimal ? this.plantGroup : this.animalGroup);
this.setState({
showAnimal: !this.state.showAnimal
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 KiB

After

Width:  |  Height:  |  Size: 225 KiB

View File

@ -5,6 +5,7 @@
color: #FFFFFF;
font-size: 56px;
top: 10%;
letter-spacing: .1em;
text-shadow: 0 1px 0 hsl(174,5%,80%),
0 2px 0 hsl(174,5%,75%),
0 3px 0 hsl(174,5%,70%),
@ -36,8 +37,7 @@
background: url('./images/3d.png') no-repeat center;
background-size: 100% 100%;
filter: drop-shadow(0px 0px 8px rgba(0, 0, 0, .5));
transition: all .5s linear;
opacity: .85;
transition: all .4s ease-in-out;
}
.grid_item:hover .three_logo {

View File

@ -24,56 +24,56 @@ const Item = styled(Paper)(({ theme }) => ({
const workList = [
{
link: '#/city',
title: '🏙 数字城市',
description: 'Lizards are a widespread group of squamate reptiles.',
title: '数字城市',
description: '🏙 3D数字城市',
image: cityImage,
three: true
},
{
link: 'https://dragonir.github.io/3d-meta-logo/',
title: '🪐 脸书Meta元宇宙Logo',
description: 'Lizards are a widespread group of squamate reptiles.',
title: '脸书Meta元宇宙Logo',
description: '🪐 Three.js + Blender 实现炫酷的Facebook元宇宙Logo.',
image: metaImage,
three: true
},
{
link: 'https://dragonir.github.io/3d-panoramic-vision/',
title: '🕵️‍ 全景侦探小游戏',
description: '使用Three.js全景功能实现侦探小游戏。',
title: '全景侦探小游戏',
description: '🕵️‍ 使用Three.js全景功能实现侦探小游戏。',
image: panoramicImage,
three: true
},
{
link: '#/earth',
title: '🌏 Low Poly地球',
description: 'Lizards are a widespread group of squamate reptiles.',
title: '地球',
description: '🌏 尽情探索3D Low Poly数字地球吧',
image: earthImage,
three: true
},
{
link: '#/cell',
title: '👻 细胞',
description: '可以查看动物细胞和植物细胞的内部组成结构。',
title: '动植物细胞结构',
description: '👻 可以查看动物细胞和植物细胞的内部组成结构。',
image: cellImage,
three: true
},
{
link: '#/lunar',
title: '🐅 虎年春节创意页面',
description: 'Lizards are a widespread group of squamate reptiles.',
title: '虎年春节创意',
description: '🐅 2022虎虎生威',
image: lunarImage,
three: true
},
{
link: 'https://dragonir.github.io/zelda-map/',
title: 'Zelda地图',
description: 'Lizards are a widespread group of squamate reptiles.',
title: '塞尔达:旷野之息地图',
description: '🗺 在地图上标记神庙、查询回忆点!',
image: zeldaImage,
},
{
link: 'https://dragonir.github.io/h5-scan-qrcode/',
title: '扫码',
description: 'Lizards are a widespread group of squamate reptiles.',
title: '浏览器扫码',
description: '📷 使用原生浏览器就可以在h5页面实现扫码功能了试试看',
image: scanImage,
}
];
@ -83,7 +83,7 @@ export default class Home extends React.Component {
return (
<div className="home" style={{ padding: '24px'}}>
<Box>
<h1 className="page_title">MY WORK LIST</h1>
<h1 className="page_title">dragonir's work list</h1>
</Box>
<Box sx={{ width: '100%' }} style={{ maxWidth: '1200px', margin: 'auto' }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>