npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-three-wrap

v3.4.3

Published

Vue component wrapping a THREE.js instance.

Downloads

35

Readme

Bare-bones Vue component wrapping a THREE.js instance.

See examples here (source).

Table of Contents

  1. Main
    1. Props
    2. start and update
    3. CSS Renderer
    4. Shader Injection
  2. Extras
    1. Raycaster
    2. Postprocessing
      1. Post Shaders
    3. Object Loader
    4. BMFont

Main

Usage:

npm install vue-three-wrap --save

Then:

<template>
    <main class="example">
        <!-- vue-three-wrap will stretch to fit its container by default -->
        <vue-three-wrap :start="start" :update="update" />
    </main>
</template>

<script>
    import VueThreeWrap from 'vue-three-wrap'
    import * as THREE from 'three'

    // can be handy to store THREE objects in an unwatched object
    const ref = {}

    export default {
        components: {
            'vue-three-wrap': VueThreeWrap
        },
        methods: {
            // called once when the scene is created
            start({ scene, camera, renderer }) {
                // example - add a cube to the scene
                const geometry = new THREE.BoxGeometry()
                const material = new THREE.MeshBasicMaterial({
                    color: 0xff0000
                })
                ref.cube = new THREE.Mesh(geometry, material)
                ref.cube.position.z = -4

                scene.add(ref.cube)
            },
            // called once per frame
            update({ scene, camera, renderer }) {
                ref.cube.rotation.y -= 0.01
            }
        }
    }
</script>

Props

| Name | Type | Default | Notes | | --------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | camera | Object | new THREE.PerspectiveCamera(75, 0.5625, 0.1, 1000) | Main camera. | | cameraType | Object, Boolean, String | perspective | perspective, orthographic, or ortho. Creates the desired camera as the scene default. | | fov | Number, String | 75 | Camera field of view. | | height | Number | -1 | Height of the canvas. -1 to take up full height of container. | | injectShaders | Boolean | false | Whether or not to inject custom shaders. See below. | | rendererOptions | Object | {} | Object of options to be passed directly to the WebGLRenderer. | | renderLoop | Boolean | true | Whether or not to call update every frame. | | renderType | String | webgl | webgl or css. Uses the CSS3DRenderer if set to css. (See below) | | start | Function({ scene, camera, renderer, slot, elements, CSS, vertexShader, fragmentShader }) | null | Function to be called once at scene creation. | | update | Function({ scene, camera, renderer, slot, elements, CSS, vertexShader, fragmentShader }) | null | Function called once per frame. | | width | Number | -1 | Width of the canvas. -1 to take up full width of container. |

start and update

start and update functions accept one object with the following parameters:

{
    // The THREE scene created by this VueThreeWrap
    scene,
        // The main camera
        camera,
        // The main renderer
        renderer,
        // The contents of the default slot
        slot,
        // An array of all valid elements in the default slot
        elements,
        // an object containing CSS renderer objects (see below)
        CSS,
        // the text of the first <script> tag in the default slot whose type is set to "shader/vertex"
        // (defaults to a standard vertex shader if none exists - see src/utils/shader-defaults.js)
        vertexShader,
        // the text of the first <script> tag in the default slot whose type is set to "shader/fragment"
        // (defaults to a pink fragment shader if none exists - see src/utils/shader-defaults.js)
        fragmentShader
}

CSS Renderer

You can use THREE's CSS renderer with vue-three-wrap:

<template>
    <vue-three-wrap :start="start" :update="update" renderType="css">
        <h2>I'm an h2</h2>
        <p>And I'm a paragraph</p>
    </vue-three-wrap>
</template>

<script>
    const ref = {}
    export default {
        methods: {
            start({ scene, camera, renderer, elements, CSS }) {
                // `elements` is a list of valid elements in the default slot
                // you'll need to manually create a new CSS3DObject for each separate element, then add it to the scene
                ref.h2 = new CSS.CSS3DObject(elements[0])
                ref.p = new CSS.CSS3DObject(elements[1])
                scene.add(ref.h2)
                scene.add(ref.p)

                // you can also do something iterative like:
                // elements.map(el => new CSS.CSS3DObject(el)).map(cssObj => scene.add(cssObj))

                // some arbitrary scaling and positioning
                // the important thing is that you can work with CSS3DObjects just like regular meshes
                ref.h2.position.set(20, 0, 0)
                ref.h2.lookAt(new THREE.Vector3(0, 20, 20))

                ref.p.position.set(-20, -20, 0)
                ref.p.lookAt(new THREE.Vector3(0, 0, 20))
                camera.position.z = 150
            },
            update() {
                ref.h2.rotation.z += 0.01
            }
        }
    }
</script>

To do so:

  1. Set the renderType prop to css.
  2. Use the elements argument in the start method to access elements in the default render slot.
  3. Create new CSS3DObjects using the CSS property passed to the start and update functions.

Otherwise, it's just like working with a normal THREE.js scene, just with usable DOM objects.

Shader Injection

Set the inject-shaders prop to true to inject some common noise functions. You can use THREE's #include convention. All return a value of -1 to 1.

  • float snoise(vec2) - 2D simplex noise.
  • float cnoise(vec2) - 2D Perlin noise.

An example fragment shader using 2D simplex noise:

<vue-three-wrap :inject-shaders="true">
    <script type="shader/fragment">
        #include <snoise>

        varying vec2 vUv;
        uniform float time;

        void main() {
            /* increase the viewing area (* 3) */
            /* scale from -1 -> 1 to 0 -> 2 (+ 1) */
            /* scale from 0 -> 2 to 0 -> 1 (/ 2) */
            float noise = (snoise((vUv + time) * 3.) + 1.) / 2.;
            vec4 dark = vec4(0., 0., 0., 1.);
            vec4 light = vec4(1.);
            gl_FragColor = mix(dark, light, noise);
        }
    </script>
</vue-three-wrap>

Note that comments in your custom shaders must use /* this format */, not // this format.

Extras

Raycaster

A class that wraps Three's raycaster.

Example:

<template>
    <vue-three-wrap ref="threeWrap" :start="start" :update="update" />
</template>

<script>
    import Raycaster from 'vue-three-wrap/extras/raycaster'

    const ref = {}

    export default {
        methods: {
            start({ camera }) {
                ref.raycaster = new Raycaster({
                    el: this.$refs.threeWrap.$el,
                    camera: camera
                })

                // (add your scene objects here)
            },
            update({ scene }) {
                // cast against all objects in the scene
                const intersects = ref.raycaster.intersectObjects(
                    scene.children
                )
                // get objects from intersections
                const intersectedObjects = intersects.map(i => i.object)
                console.log(intersects, intersectedObjects)
            }
        }
    }
</script>

Constructor

Defaults shown.

new Raycaster({
    // the area to check on mouseover
    el: document.querySelector('canvas'),

    // the camera that will be doing the raycasting
    camera: null,

    // whether or not to print debug messages
    debug: false,

    // raycaster (optional - will create automatically if not specified)
    raycaster: null
})

Properties

| Name | Type | Notes | | ------------- | ------ | ------------------------------------------------------- | | interpolatedX | Number | The normalized relative mouse X position, from -1 to 1. | | interpolatedY | Number | The normalized relative mouse Y position, from -1 to 1. | | mouseX | Number | The latest mouseX position relative to the container. | | mouseY | Number | The latest mouseY position relative to the container. |

Methods

| Name | Arguments | Notes | | ---------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | init | options (same as constructor) | Initializes this raycaster. Same method as called by constructor. | | updateMouse | mouse event | Updates relative mouse coordinates. Called internally. | | cast | { coordinates: { x: Number, y: Number }, camera: THREE.Camera } | Raycast and save the results internally. If coordinates are unspecified, uses the last coordinates set by updateMouse. If camera is unspecified, uses the camera added during instantiation. | | intersectObject | (object, recursive, optionalTarget, coordinates, camera) | Calls cast() with the given coordinates and camera, then runs intersectObject with the first three arguments. | | intersectObjects | (objects, recursive, optionalTarget, coordinates, camera) | Calls cast() with the given coordinates and camera, then runs intersectObjects with the first three arguments. | | destroy | None | Destroys the event listener created during init. |

Postprocessing

You can use the postprocessor from the examples very easily with the custom-renderer prop:

<template>
    <vue-three-wrap
        :custom-renderer="composer"
        :start="start"
        :update="update"
    />
</template>

<script>
    import * as THREE from 'three'
    import QuickComposer from 'vue-three-wrap/extras/quick-composer'
    import DotScreenShader from 'vue-three-wrap/shaders/DotScreenShader'
    import RGBShiftShader from 'vue-three-wrap/shaders/RGBShiftShader'

    const ref = {}

    export default {
        data() {
            return {
                composer: null
            }
        },
        methods: {
            start({ scene, camera, renderer }) {
                // position camera
                camera.position.z = 10

                // add cube
                const geo = new THREE.BoxGeometry()
                const mat = new THREE.MeshLambertMaterial({ color: 0xffcccc })
                ref.box = new THREE.Mesh(geo, mat)
                scene.add(ref.box)

                // add sun
                const sun = new THREE.DirectionalLight()
                sun.position.set(5, 5, 5)
                scene.add(sun)

                // build composer
                this.composer = QuickComposer({
                    scene,
                    camera,
                    renderer,
                    passes: [DotScreenShader, RGBShiftShader]
                })

                // Set a uniform of a pass
                // Note that passes are 1-indexed when using QuickComposer
                this.composer.getPass(1).uniforms.scale.value = 4
                // A quicker way to do the same thing:
                this.composer.setUniform(1, 'scale', 4)
            },
            update() {
                // rotate the box
                ref.box.rotation.x += 0.002
                ref.box.rotation.y -= 0.005

                // move the box in a circle
                const d = Date.now() * 0.0015
                ref.box.position.set(Math.sin(d) * 2, Math.cos(d) * 2, 0)
            }
        }
    }
</script>

To use:

  1. Import QuickComposer from vue-three-wrap/extras/quick-composer.

    1. If you want more control of your composer, you can also import EffectComposer from vue-three-wrap/extras/effect-composer.
  2. Set the custom-renderer prop in vue-three-wrap to an instance of the QuickComposer.

  3. Instantiate the QuickComposer with the following options as an object:

    {
        // these three can just be passed from your start/update functions
        scene,
        camera,
        renderer,
    
        // an array of shader objects (see below)
        passes: []
    }

TODO: document EffectComposer and QuickComposer

Post Shaders

vue-three-wrap comes with some complete shaders in the form of JS objects:

  • DotScreenShader
  • RGBShiftShader

You can import any existing shader from vue-three-wrap/shaders/YourDesiredShader.

You can also create your own by making an object with uniforms, vertexShader, and fragmentShader properties:

// ExampleShader.js
export default {
    uniforms: {
        // Each uniform ust be an object with a `value` property
        yourUniform: { value: 0}
    },
    vertexShader: 'A string containing your WebGL vertex shader',
    fragmentShader: 'A string containing your WebGL fragment shader'
}

You can then use this shader as a pass in the QuickComposer or EffectComposer. Writing shaders is beyond the scope of this readme - take a look at The Book of Shaders for more information.

Object Loader

You can import .gltf and .glb files, the format that Three prefers, using the load-gltf extra.

<template>
    <vue-three-wrap class="object-loader" :start="start" />
</template>

<script>
    // to use the loadObjects module:
    import { loadObjects } from 'vue-three-wrap/extras/load-gltf/'
    // to use the default full scene loader:
    import loadScene from 'vue-three-wrap/extras/load-gltf'

    export default {
        methods: {
            async start({ scene, camera, vertexShader, fragmentShader }) {
                // you can load a full scene...
                // const glb = await loadScene('/assets/scene.glb')
                // scene.add(glb.scene)

                // ...or individual objects
                const objects = await loadObjects('/assets/scene.glb')
                objects.forEach(obj => scene.add(obj))

                // remember to add some lighting

                // place and rotate the camera
                camera.position.set(-4, 4, 4)
                camera.lookAt(new THREE.Vector3(0, 0, 0))
            }
        }
    }
</script>

BMFont

vue-three-wrap comes with methods to handle loading and displaying text with BMFonts. To use:

  1. Convert your font to a BMFont with a tool like msdf-bmfont. Place the created .fnt and .png files in your project.
  2. Import the bmfont method from vue-three-wrap/extras/bm-fontand use like this:
<template>
    <vue-three-wrap class="bmfont" :start="start" />
</template>

<script>
    import bmFont from 'vue-three-wrap/extras/bm-font'

    export default {
        methods: {
            async start({ scene }) {
                // `result` is an object with properties { font, texture, geometry, mesh, material }
                const result = await bmFont({
                    // path to .fnt and .png files
                    fnt: '/your-font-file.fnt',
                    png: '/your-font-image.png',

                    // the text you want to display
                    text: 'Your text here!',

                    // OPTIONAL: options to pass to material - see the MSDFShader method here:
                    // https://tympanus.net/codrops/2019/10/10/create-text-in-three-js-with-three-bmfont-text/
                    opts: {
                        // fragmentShader: `void main() { ... }`,
                        // vertexShader: `...`
                        // etc
                    }
                })

                // mesh will be very large by default, so we're moving it away from the camera here
                // mesh is also instantiated upside-down, so the bmFont method rotates it 180deg on the X axis
                scene.add(result.mesh)
                result.mesh.position.z = -130
            }
        }
    }
</script>