简体   繁体   中英

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

I am trying to create a custom post header background for a forum website. But because I always want to make my life more difficult I tried to make it fancy. I also need to link to that script in every post.

When I duplicate the post (or simulate it by just ctrl+a ctrl+c ctrl+v ctrl+v the HTMP code) only the last post gets any smoke effect background. I tried to add the function to the window on init and loop it for every element with the SiffrinSmoke class but still only one smoke is displayed - at the bottom.

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%;}
    }

Import THREE:

<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();
}

Do you know how to apply that same effect as background for every element with that class? Maybe another tool would be better for the job?

EDIT: Solution The Smoke init function accepts the background elements from before and returns relevant objects

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

Then in window.onloadI have this loop

        smokeBackgrounds = document.getElementsByClassName('SiffrinSmoke');

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

I also edited the animate functions

    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();

and that works!

It's because you're only creating one canvas, and just appending that to each "smokeBackgrounds" in the loop. But each one overwrites the last one, because there's only one canvas (renderer.domElement).

There's a few ways you could solve it:

  1. Create a loop outside of this code that loops through smokeBackgrounds, and calls initiateSmoke(smokeBackgrounds[i]), and then change that loop towards the end of the method to use the passed-in dom element.
  2. Clone the canvas itself, and assign each new instance a smokeBackgrounds.
  3. (Hard) Create multiple threejs scenes within the code, and assign each a position on the page. This way you can stretch the canvas to the full-page, and just move around the smoke positions to be associated with where the HTML elements are. The advantage of this is it's lighter because you won't have multiple canvas's running.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM