软件技术-Aframe-中文汉字解决方案

96
zhyuzh3d Excellent
0.9 2019.04.09 22:23* 字数 914

欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】
【汇总】2019年4月专题


如何在3D场景中正常显示中文汉字?

之前曾经在这个文章里面记录了Aframe的文字实现方法:
软件技术-WebVR-AFrame文字的实现

这里主要是记录结合React使用的情况。

基于上一篇文章的项目,软件技术-React-Aframe网站开发

修改用于实现每个按钮的元素component/linkBox.js文件做了一些改进,以下是修改后的代码:

import React from "react";
import ReactDOM from "react-dom";
import h from "react-hyperscript"
import 'aframe';
import 'aframe-text-geometry-component';
import 'aframe-html-shader';

let gen = (position, size, clr, txt, href, label, labelId) => {
    return h('a-entity', {
        position,
        scale: '' + size + ' ' + size + ' ' + size,
        href,
    }, [
        h('a-entity', {
            geometry: "primitive: plane",
            position: "0 -0.75 0",
            scale: "0.25 0.25 0.25",
            material: "shader:html;\
            target:#" + labelId + ";\
            transparent:true;\
            ratio:height;\
            fps:1;"
        }),
        h('a-box', {
            material: "side:double;color:" + clr + ";\
            blending:additive;\
            opacity:0.2,\
            metalness:0.8",
        }),
        h('a-box', {
            id: 'tar',
            class: 'clickable',
            material: "side:double;color:" + clr + ";\
            blending:additive;\
            opacity:0.2,\
            metalness:0.8",
            scale: '1.2 1.2 1.2'
        }),
        h('a-entity', {
            material: 'metalness: 0.85;color:' + clr,
            position: "-0.4 -0.08 0",
            'text-geometry': "value:" + txt.toUpperCase() + "; \
            font: #optimerBoldFont;\
            height:0.1;\
            size:0.2;\
            curveSegments:1"
        }),
    ])
}

function createLabelDom(labelId, label,color) {
    //添加html标签元素
    let dom = document.getElementById('aframeTextLabels');
    if (!dom) {
        dom = document.createElement("div");
        dom.setAttribute("id", "aframeTextLabels")
        document.getElementsByTagName("body")[0].appendChild(dom)
    }
    let labelDom = document.createElement("div");
    labelDom.setAttribute("id", labelId);
    labelDom.innerHTML = label
    labelDom.style.position = 'absolute';
    labelDom.style['z-index'] = '-100';
    labelDom.style.color = color;
    labelDom.style['font-size'] = '100px';
    labelDom.style['font-weight'] = 'bold';
    dom.appendChild(labelDom)
}

export default class myComponent extends React.Component {
    constructor(props) {
        super(props);
        this['labelId'] = ('label' + Math.random()).replace('.', '')
        this.state = {
            use: false
        };
    }

    componentDidMount() {
        setTimeout(() => {            
            createLabelDom(this['labelId'], this.props.label || this.props.text,this.props.color)
            this.setState({
                use: true
            });
            this.render()
            
            //添加点击功能
            let rdom = ReactDOM.findDOMNode(this);
            let tarEl = rdom.querySelector('#tar');
            tarEl.addEventListener('click', function () {
                location = rdom.getAttribute('href')
            });            
        }, 1000);
    }
    render() {
        return this.state.use ? gen(this.props.position,
            this.props.size,
            this.props.color,
            this.props.text,
            this.props.href,
            this.props.label,
            this['labelId'],
        ) : null
    }
}

主要有几个地方注意:

  • 引入了aframe-html-shader,它可以将一个普通的html元素变为一个图片,然后再贴到Aframe的3D场景中的模型上,并且能同步,就是说原来的html元素改变的时候,3D场景上的贴图会自动改变。

npm上的aframe-html-shader官方说明地址点这里

  • 增加了单独的function createLabelDom(labelId, label)函数用于创建一个标准的html元素,但是注意这里的labelId,我们将依靠这个id将html元素和3D元素绑定到一起,所以,这个id应该是唯一的。

  • constructor(props)React元素实例的初始化构建方法中我们增加了this['labelId'] = ('label' + Math.random()).replace('.', ''),我们用随机数生成唯一性的id,注意这里一定要去掉随机数的小数点,因为<div id='xxx'这里的xxx是不能包含小数点和特殊字符的。

  • 我们在componentDidMountReact元素加载完成方法中增加了createLabelDom(this['labelId'], this.props.label || this.props.text),这里使用了上面创建的this['labelId']this.props.label || this.props.text表示如果有label就用label,没有就用text。注意这里使用了setTimeout延迟添加,这样的好处是可以避免初始页面的时候html文字闪现。

  • 在最上面的let gen = (position, size, clr, txt, href, label, labelId)方法中我们增加了labelId参数,需要注意的是我们把新的文字放在了第一个h('a-entity', {geometry: "primitive: plane",...,这里是表示html元素转换的图片贴在一个plane平面物体上,下面的material: "shader:html;\target:#" + labelId + ";\实现了aframe-html-shader插件的启用和labelId的绑定。这里之所以放在第一个是防止被方盒a-box遮挡,一旦遮挡即使方盒透明也看不到文字,这应该是个bug。

  • 另外一个问题是在浏览器的控制台总会不断地输出警告aframe-master.js:50449 THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().,这是由于aframe-html-shader插件比较老,新的 three.js有些函数名改变了,而aframe-html-shader插件没有更新,我们可以自己手工找到node_modules/aframe-html-shader下面的js文件,搜索到两个nearestPowerOfTwo将它替换为floorPowerOfTwo就可以了。

Aframe的确制造了不少麻烦,有些时候想想还不如直接使用Three.js更自由些。但是考虑到开发效率,毕竟Aframe也提供了更多的方便之处。


欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】


每个人的智能新时代

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~


END

人工智能DBD
Gupao