THREE.js 旋转烟雾效果仅适用于一个元素,而不适用于所有元素

THREE.js revolving smoke effect works only for one element instead of applying to all elements

我正在尝试为论坛网站创建自定义 post 页眉背景。但是因为我总是想让我的生活变得更加艰难,所以我试图让它变得花哨。我还需要 link 每个 post.

中的那个脚本

当我复制 post(或仅通过 ctrl+a ctrl+c ctrl+v ctrl+v HTMP 代码模拟它)时,只有最后一个 post 获得任何烟雾效果背景。我试图在初始化时将函数添加到 window 并使用 SiffrinSmoke class 为每个元素循环它,但仍然只显示一根烟雾 - 在底部。

HTML:

<div class="SiffrinBody">
    <div class="SiffrinHeader">
        <div class="SiffrinSmoke">
            <!--h1>Siffrin<br>Drauglir</h1-->
            <h1 data-heading="Siffrin&#xa;Drauglir"></h1>
        </div>
        <div class="SiffrinCanvasBackground" id="SiffrinCanvasBackground"></div>
        <div class="SiffrinCanvasForeground" id="SiffrinCanvasForeground"></div>
        <img id="SiffrinHeaderBackground" src="https://i.imgur.com/iYnkBdZ.jpg">
        <img id="SiffrinWolf" src="https://i.imgur.com/MBLqG00.png">
    </div>
</div>

CSS:

 @import url('https://fonts.googleapis.com/css2?family=Arvo:ital@0;1&family=Carter+One&family=Dosis:wght@400;500;600&display=swap');

    .SiffrinBody .SiffrinHeader .SiffrinCanvasBackground{
        height: 330px;
        width: 656px;
        position: absolute;
        left: inherit;
        top: inherit;
        z-index: 5;
    }

    .SiffrinBody .SiffrinHeader .SiffrinCanvasForeground{
        height: 330px;
        width: 656px;
        position: absolute;
        left: inherit;
        top: inherit;
        z-index: 7;
    }

    .SiffrinBody .SiffrinHeader img{
        position: absolute;
        height: 330px;
        width: 656px;
        grid-row: 1;
    }

    .SiffrinBody .SiffrinHeader .SiffrinSmoke{
        width: 328px;
        height: 330px;
        position: relative;
        z-index: 6;
        float: right;
        display: block;
    }

    .SiffrinBody .SiffrinHeader .SiffrinSmoke canvas{
        position: absolute;
        top: 0;
        left: 0;
        z-index: -4;
    }

    .SiffrinBody .SiffrinHeader #SiffrinHeaderBackground{
        z-index: 4;
    }

    .SiffrinBody .SiffrinHeader #SiffrinWolf{
        z-index: 6;

    }

    .SiffrinBody .SiffrinHeader h1{
        font-family: Carter One;
        z-index: 1000;
        color: white;
        grid-column: 2;
        font-size: 50;
        margin-top: 32%;
        text-align: center;
        background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/209981/6963bbf342d87b3a2150bd8f59682b89.jpg);
        -webkit-background-clip: text;
        background-size: cover;
        width: 100%;
        color: transparent;
        font-weight: 900;
        display: block;
        white-space: pre-wrap !important;
        line-height: 1.1em;
        text-shadow:  2px 2px 6px rgba(46,146,211,0.8);
    }

    .SiffrinBody .SiffrinHeader h1::before{
        content: attr(data-heading);
        position: relative;
        left: 0;
        top: 0;
        width: 100%;
        background: linear-gradient(45deg, rgba(255,255,255,0) 45%,rgba(255,255,255,0.8) 50%,rgba(255,255,255,0) 55%,rgba(255,255,255,0) 100%);
        -webkit-background-clip: text;
        color: transparent;
        mix-blend-mode: screen;
        animation: SiffrinShine 15s infinite linear;
        background-size: 200%;
    }

    .SiffrinBody .SiffrinHeader #SiffrinName{
        z-index: 22;
        transform: scale(0.8);
        left: 150px;
    }

    .SiffrinBody .SiffrinHeader{
        grid-row: 1;
        mask-image: linear-gradient(
            to top,
            rgba(255, 255, 255, 0) 0,
            rgba(255, 255, 255, 1) 15px
        );
    }

    .SiffrinBody .SiffrinHeader > *{
        user-select: none;
    }

    .SiffrinBody{
        color: white;
        display: grid;
        grid-template-rows: 330px auto auto;
        grid-template-columns: 1fr;
        margin: 0;
        padding: 0;
        width: 656px;
        /*background-image: linear-gradient(#bad4eb, #bad4eb, #a7c8e7, #4789c6, #037ccf);*/
        background-image: url("https://i.imgur.com/UqFcRzS.jpg");
        background-repeat: repeat;
    }

    .SiffrinBody *::selection{
        background: rgba(225, 0, 255, 0.3);
        color: inherit;
    }

    .SiffrinBody .SiffrinHeader{
        /*
        z-index: 7;
        text-align: center;
        font-family: Oleo Script;
        margin: auto;
        font-size: 30px;
        transition-duration: 2s;
        transition-timing-function: ease-in-out;
        background-image: radial-gradient(#0e111800, #232b3e00, #24212a00, #403b6600);
        user-select: none;
        */
    }

    .SiffrinBody .SiffrinBody:hover .SiffrinHeader{
        /*
        letter-spacing: 0.35em;
        transition-duration: 3s;
        transition-timing-function: ease-in-out;
        transform: translate(0px, 40px);
        */
    }

    .SiffrinBody .SiffrinText{
        z-index: 7;
        margin: 10px 50px;
        padding: 20px;
        background-color: rgba(0, 0, 0, 0.5);
        font-family: Dosis;
        font-size: 17px;
        margin-bottom: 0;
        line-height: 1.235em;
        font-weight: 500;
    }

    .SiffrinBody .SiffrinText::before{
  
        border-top: 3px solid blue;
        border-right: 3px solid blue;
    }

    .SiffrinBody .SiffrinText::after{

        border-bottom: 3px solid orange;
        border-left: 3px solid orange;
    }

    .SiffrinBody .SiffrinText p{
        padding: 0;
        margin-bottom: 1em;
    }

    .SiffrinBody .SiffrinText p u,
    .SiffrinBody .SiffrinText p i,
    .SiffrinBody .SiffrinText p q{
        font-family: Arvo;
        font-size: 16px;
    }

    .SiffrinBody .SiffrinText p u{
        text-decoration: none;
        color:rgb(250, 210, 255);
    }

    .SiffrinBody .SiffrinText p u::before {
        content: "“"
    }

    .SiffrinBody.SiffrinText p u::after {
        content: "”"
    }

    .SiffrinBody .SiffrinText p i{
        font-family: Arvo;
        color:rgb(250, 210, 255);
        font-style: italic;
    }

    .SiffrinBody .SiffrinText p q{
        text-decoration: none;
        color:rgb(219, 223, 255);
    }

    .SiffrinBody .SiffrinText p:last-child{
        margin: 0;
    }

    .SiffrinBody .SiffrinText p:first-child{
        margin-top: 0;
    }

    .SiffrinBody .SiffrinFooter{
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 10px;
        margin: 0;
    }

    .SiffrinBody .SiffrinFooter p{
        text-align: center;
        font-size: 20px;
        color: #f8f7fd;
        text-align: center;
    }

    @keyframes SiffrinShine {
        0% {background-position: 110%;}
        60% {background-position: -90%;}
        100% {background-position: -90%;}
    }

导入三个:

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>

JS:

function initiateSmoke() {
        smokeBackgrounds = document.getElementsByClassName('SiffrinSmoke');
        clock = new THREE.Clock();
        renderW = smokeBackgrounds[0].offsetWidth;
        renderH = smokeBackgrounds[0].offsetHeight;
        renderer = new THREE.WebGLRenderer({ alpha: true });
        renderer.setSize( renderW, renderH );

        scene = new THREE.Scene();
    
        camera = new THREE.PerspectiveCamera( 75, renderW / renderH , 1, 10000 );
        camera.position.z = 1300;
        scene.add( camera );
    
        geometry = new THREE.CubeGeometry( 200, 200, 200 );
        material = new THREE.MeshLambertMaterial( { color: 0xaa6666, wireframe: false } );
        mesh = new THREE.Mesh( geometry, material );
        cubeSineDriver = 0;
    
        textGeo = new THREE.PlaneGeometry(300,300);
        THREE.ImageUtils.crossOrigin = ''; //Need this to pull in crossdomain images from AWS

        light = new THREE.DirectionalLight(0xffffff, 1);
        light.position.set(-1,0,1);
        scene.add(light);
    
        smokeTexture = THREE.ImageUtils.loadTexture('https://s3-us-west-2.amazonaws.com/s.cdpn.io/95637/Smoke-Element.png');
        smokeMaterial = new THREE.MeshLambertMaterial({color: 0x333333, opacity: 1, map: smokeTexture, transparent: true});
        smokeGeo = new THREE.PlaneGeometry(650,650);
        smokeParticles = [];

        for (p = 0; p < 36; p++) {
            particle = new THREE.Mesh(smokeGeo,smokeMaterial);
            particle.position.set(Math.random() * 950 - 500, Math.random() * 620 - 300, Math.random() * 200 - 100);
            particle.rotation.z = Math.random() * 360;
            particle.scale.set(2,2,2);
            scene.add(particle);
            smokeParticles.push(particle);
        }
        for(i = 0; i < smokeBackgrounds.length; i++) {
            smokeBackgrounds[i].appendChild( renderer.domElement );
        }
    }
        
    function animateSmoke() {
        // note: three.js includes requestAnimationFrame shim
        delta = clock.getDelta();
        requestAnimationFrame( animateSmoke );
        evolveSmoke();
        render();
    }
        
    function evolveSmoke() {
        var sp = smokeParticles.length;
        while(sp--) {
            smokeParticles[sp].rotation.z += (delta * 0.2);
        }
    }

    function render() {
        mesh.rotation.x += 0.005;
        mesh.rotation.y += 0.01;
        cubeSineDriver += .01;
        mesh.position.z = 100 + (Math.sin(cubeSineDriver) * 500);
        renderer.render( scene, camera );
    }

    window.onload = function Snowfall(){
        initiateSmoke();
        animateSmoke();
}

您知道如何为具有 class 的每个元素应用与背景相同的效果吗?也许另一个工具更适合这项工作?

编辑:解决方案 Smoke init函数接受之前的背景元素和returns个相关对象

function initiateSmoke(smokeBackground) {
[...]
        return({
            "renderer": renderer,
            "scene": scene,
            "camera": camera,
            "mesh": mesh,
            "cubeSineDriver": cubeSineDriver,
            "smokeParticles": smokeParticles
        })
}

然后在window.onloadI有这个循环

        smokeBackgrounds = document.getElementsByClassName('SiffrinSmoke');

        for(i = 0; i < smokeBackgrounds.length; i++) {
            smokeScenes[i] = initiateSmoke(smokeBackgrounds[i]);
            smokeBackgrounds[i].appendChild(smokeScenes[i]['renderer'].domElement);
        }
        
        animateSmoke();

我还编辑了动画函数

    function animateSmoke() {
        delta = clock.getDelta();
        requestAnimationFrame(animateSmoke);
        evolveSmoke();
        render();
    }
        
    function evolveSmoke() {
        for(i = 0; i < smokeScenes.length; i++) {
            smokeScene = smokeScenes[i];
            var sp = smokeScene['smokeParticles'].length;
            while(sp--) {
                smokeScene['smokeParticles'][sp].rotation.z += (delta * 0.2);
            }
        }
    }

    function render() {
        for(i = 0; i < smokeScenes.length; i++) {
            smokeScene = smokeScenes[i];
            smokeScene['mesh'].rotation.x += 0.005;
            smokeScene['mesh'].rotation.y += 0.01;
            smokeScene['cubeSineDriver'] += .01;
            smokeScene['mesh'].position.z = 100 + (Math.sin(smokeScene['cubeSineDriver']) * 500);
            smokeScene['renderer'].render( smokeScene['scene'], smokeScene['camera']);
        }
    }

    let smokeScenes = []
    let clock = new THREE.Clock();

这很有效!

这是因为您只创建了一个 canvas,并将其附加到循环中的每个“smokeBackgrounds”。但是每一个都会覆盖最后一个,因为只有一个 canvas (renderer.domElement).

有几种方法可以解决它:

  1. 在此代码之外创建一个循环,循环通过 smokeBackgrounds,并调用 initiateSmoke(smokeBackgrounds[i]),然后将该循环更改为使用 passed-in dom元素。
  2. 克隆 canvas 本身,并为每个新实例分配一个 smokeBackgrounds。
  3. (困难)在代码中创建多个 threejs 场景,并在页面上为每个场景分配一个位置。通过这种方式,您可以将 canvas 拉伸到 full-page,只需移动烟雾位置即可与 HTML 元素所在的位置相关联。这样做的好处是它更轻,因为你不会有多个 canvas 的 运行.