StudioCode.devMIDI applications that run in your browser

svg-knob

A flexible and customizable knob for your web applications.

Open the anatomy.html file to view a description of how the knob is constructed.

Usage

Check the demo.html file for examples.

.knob {
    width: 100px;
}

<svg class="knob" id="knob"></svg>

Without ES6 module support:

<script src="dist/index.umd.js"></script>
<script>
    var k = new svgKnob('#knob', { /* config... */ });   
</script>

With ES6 module support:

import Knob from './svg-knob.js';
const k = new Knob('#knob', { /* config... */ });        

change the value:

k.value = 42;

listen to the change events:

document.getElementById("slider").addEventListener("change", function(event) {
    let [knob_id, knob_value] = [event.target.id, event.detail];
});    

Usage with React

Quick example for a svg-knob and a linked div displaying the value transmitted through the knob's onChange event.

Simple SvgKnob component (2 files):

components/SvgKnob/index.jsx :

import React, { Component } from 'react';
import Knob from './knob';

function Value(props) {
    return <div className="value">{props.value}</div>;
}

class SvgKnobWithValueSimplerSyntax extends Component {

    state = { value: 0 };

    handleChange = e => this.setState({value: e.detail});

    render() {
        return (
            <div className="knob">
                <Knob onChange={this.handleChange} />
                <Value value={this.state.value} />
            </div>
        );
    }
}

export default SvgKnob;

components/SvgKnob/knob.jsx :

import React, { Component } from 'react';
import SvgKnob from 'svg-knob';

class Knob extends Component {

    handleChange = e => {if (this.props.onChange) this.props.onChange(e);};

    componentDidMount() {
        this.k = new SvgKnob(this.dom);
        this.dom.addEventListener("change", this.handleChange);
    }

    // Not really necessary, but will slightly improve the rendering performance.
    shouldComponentUpdate() {
        return this.k === null;
    }

    render() {
        return (
            <svg ref={elem => this.dom = elem} />
        );
    }
}

export default Knob;

React App:

App.js :

import React, {Component} from 'react';
import './App.css';
import SvgKnob from "./components/SvgKnob/";

class App extends Component {
    render() {
        return (
            <div className="App">
                <SvgKnob />
            </div>
        );
    }
}

export default App;

App.css:

.knob {
    width: 100px;
}

.knob .value {
    text-align: center;
}

Options

let defaults = {
    // User configurable properties. The colors are defined in the 'palettes', later on.

    label: false,
    rotation: CW,

    default_value: 0,
    initial_value: 0,
    value_min: 0.0,
    value_max: 100.0,
    value_resolution: 1,        // null means ignore

    // split knob:
    center_zero: false,
    center_value: null,         // if null, the value will be computed from the min and max in the init() method
    center_gap: 4,              // only used when center_zero=true; is the width of the gap between the left and right track around the zero value.

    // position:
    zero_at: 270.0,             // [deg] (polar) the 0 degree will be at 270 polar degrees (6 o'clock).
    angle_min: 30.0,            // [deg] Angle in knob coordinates (0 at 6 0'clock)
    angle_max: 330.0,           // [deg] Angle in knob coordinates (0 at 6 0'clock)

    // background disk:
    bg_radius: 32,
    bg_border_width: 1,

    // track background:
    track_bg_radius: 40,
    track_bg_width: 8,

    // track:
    track_radius: 40,
    track_width: 8,

    // cursor
    cursor_radius: 18,          // same unit as radius
    cursor_length: 10,
    cursor_width: 4,

    // appearance:
    palette: 'light',
    bg: false,
    track_bg: true,
    track: true,
    cursor: false,
    // CSS class names
    linecap: 'butt',                    // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap
    value_text: true,
    value_position: HALF_HEIGHT + 8,    // empirical value: HALF_HEIGHT + config.font_size / 3
    format: v => v,                     // formatting of the displayed value
    font_family: 'sans-serif',
    font_size: 25,

    font_weight: 'bold',
    markers: 0,                         // number of markers; 0 or false to disable
    markers_radius: 40,
    markers_length: 8,
    markers_width: 2,

    class_bg: 'knob-bg',
    class_track_bg : 'knob-track-bg',
    class_track : 'knob-track',
    class_value : 'knob-value',
    class_cursor : 'knob-cursor',
    class_markers: 'knob-markers',

    // mouse wheel support:
    mouse_wheel_acceleration: 1,

    // callback function:
    onchange: null                      // provides an alternative to the 'change' event   
};

light : {
    bg_color: '#E0E0E0',
    bg_border_color: '#BDBDBD',
    track_bg_color: '#CFD8DC',
    track_color_init: '#64B5F6',
    track_color: '#42A5F5',
    cursor_color_init: '#64B5F6',
    cursor_color: '#42A5F5',
    markers_color: '#9E9E9E',
    font_color: '#424242',
},
light2 : {
    bg_color: '#B1DAEE',
    bg_border_color: '#569DC0',
    track_bg_color: '#B1DAEE',
    track_color_init: '#569DC0',
    track_color: '#1D6D93',
    cursor_color_init: '#569DC0',
    cursor_color: '#1D6D93',
    markers_color: '#3680A4',
    font_color: '#1D6D93',
},
dark: {
    bg_color: '#000000',
    bg_border_color: '#569DC0',
    track_bg_color: '#424242',
    track_color_init: '#FDD835',
    track_color: '#FFEC00',
    cursor_color_init: '#569DC0',
    cursor_color: '#FDD835',
    markers_color: '#3680A4',
    font_color: '#FFEA00',
}

License

This project is licensed under the MIT License - see the LICENSE file for details

© StudioCode - Made with 11ty and tailwindcss.