Creating a color picker

We will implement a color picker using the slider from the previous section. A color picker is a UI control that allows the user to select a color. Usually, the color picker is shown as a small, colored rectangle, and when the user clicks on it, a window is opened with controls to change the color. To follow the code snippets, open the chapter04/02-color-picker.html file. Refer to the following screenshot:

Creating a color picker

The color picker selector and window that uses the sliders from the previous section

We will implement the color picker using the reusable chart pattern. This will allow us to modularize the components in two parts, the color picker selector and the color picker window. We begin by creating the color picker selector.

The color picker selector

The control element will be shown as a small rectangle. When the user clicks on the rectangle, the color picker will appear, and clicking on the control again will set the value. We will create a color picker using the CIELAB 1976 color model, which is informally known as Lab. In this model, L is for lightness, and the a and b parameters represent colors. This color model aims to be more perceptually uniform than other models, which means that changes in the color value are perceived as changes of about the same visual importance. We create the structure of the color picker using the reusable chart pattern as follows:

function labColorPicker() {

    // Selector Attributes

    // Selector shape
    var width = 30,
        height = 10;

    // Default Color
    var color = d3.lab(100, 0, 0);

    // Charting function
    function chart(selection) {
        selection.each(function() {
            // Creation of the color picker...
        });
    }

    // Width and height accessor methods...

    // Color Accessor
    chart.color = function(value) {
        if (!arguments.length) { return color; }
        color = d3.lab(value);
        return chart;
    };

    return chart;
}

The chart.color method receives a color in any format that can be converted by d3.lab and returns the picker color as a d3.lab object with the current color of the picker. We can also add accessors for the width and height (not shown for brevity). To use the color picker, we need to create a container group for it and use the call method to create the color selector:

// Create the svg figure...

// Create the color picker and set the initial color
var picker = labColorPicker().color('#a40000'),

// Create a group for the color picker and translate it.
var grp = svg.append('g')
    .attr('transform', 'translate(' + [offset, offset] + ')')
    .call(picker);

We will translate the group to add a margin between the color picker selector and the surrounding elements. In the charting function, we will create a selection with a rectangle, bind the current color to that selection, and create the rectangle on enter:

function chart(selection) {
    selection.each(function() {
        // Create the container group and rectangle
        var group = d3.select(this),
            rect = group.selectAll('rect'),

        // Bind the rectangle to the color item and set its
        // initial attributes.
        rect.data([chart.color()])
            .enter().append('rect')
            .attr('width', width)
            .attr('height', height)
            .attr('fill', function(d) { return d; })
            .attr('stroke', '#222')
            .attr('stroke-width', 1);
    });
}

This will create the picker rectangle with the initial color.

Adding the color picker window

The color picker window should show up if the color selector is clicked on and hide it if the selector is clicked on a second time. We will use a div element to create the color picker window:

        // Bind the rectangle to the data
        rect.data([chart.color()])
            .enter().append('rect')
            // set more attributes ...
            .on('click', chart.onClick);

The openPicker function receives the data item bound to the rectangle. When the user clicks on the rectangle, the openPicker function will be invoked. This method will create the color picker window (a div) or remove it if already exists:

var openPicker = function(d) {
    // Select the color picker div and bind the data.
    var div = d3.select('body').selectAll('div.color-picker')
        .data([d]);

    if (div.empty()) {
        // Create the container div, if it doesn't exist.
        div.enter().append('div')
            .attr('class', 'color-picker'),
    } else {
        // Remove the color picker div, if it exists.
        d3.select('body').selectAll('div.color-picker')
            .remove();
    }
};

Here, we detect whether the element exists using the empty method. If the selection is empty, we create the div and set its attributes. If the selection is not empty, we remove the color picker window. We want the color picker window to appear near the rectangle; we will use the position of the pointer to locate the window to the right-hand side of the selector. To position the color picker window, we need to set its position to absolute, and set its top and left offsets to appropriate values. We also set a provisional width, height, and background color for the div:

// Create the container div, if it doesn't exist.
div.enter().append('div')
    .attr('class', 'color-picker')
    .style('position', 'absolute')
    .style('left', (d3.event.pageX + width) + 'px')
    .style('top', d3.event.pageY + 'px')
    .style('width', '200px')
    .style('height', '100px')
    .style('background-color', '#eee'),

We now have a div element that is displayed when the user clicks on it and hidden when the user clicks again. Most importantly, the window div is bound to the same data item as the rectangle, and the this context in the onClick method is the rectangle node. We can now create the color picker window as a reusable chart and bind it to the color picker selector.

The color picker window

Let's review what we have done so far. The color picker has two parts: the color picker selector and the color picker window. The color picker window is a div that appears when the user clicks on the selector and disappears if the user clicks again. We will use the reusable chart pattern to create the color picker window. Refer to the following screenshot:

The color picker window

Components of the color picker; we will use the slider from the previous section

We can create the color picker content as an independent chart. For simplicity, this time, we won't add the width, height, and margins as configurable attributes. In this case, there are many elements that need to be created and positioned in the figure; we will only show you the most important ones:

function labColorPickerWindow() {

    // Chart Attributes...
    var margin = 10,
        // set more attributes...
        width = 3 * margin + labelWidth + sliderWidth + squareSize,
        height = 2 * margin + squareSize;

    function chart(selection) {
        selection.each(function(data) {
            // Select the container div and set its style
            var divContent = d3.select(this);
            // set the divContent size and position...

            // Create the SVG Element
            var svg = divContent.selectAll('svg')
                .data([data])
                .enter().append('svg'),

            // set the svg width and height...
            // add more elements...
        });
    }

    return chart;
};

Again, we have used the basic structure of a reusable chart. Remember that in the selection.each function, the data is the color of the selector, and the context is the container div. We begin by adding the square that will show you the selected color:

    // Add the color square
    var colorSquare = svg.append('rect')
        .attr('x', 2 * margin + sliderWidth + labelWidth)
        .attr('y', margin)
        .attr('width', squareSize)
        .attr('height', squareSize)
        .attr('fill', data);

This will put the square to the right-hand side of the window. Next, we will create a scale to position each slider vertically. The rangePoints method of the scale allows us to evenly distribute the sliders in the vertical space:

    // Scale to distribute the sliders vertically
    var vScale = d3.scale.ordinal()
        .domain([0, 1, 2])
        .rangePoints([0, squareSize], 1);

We will use this scale to set the position of the groups that will contain the slider elements for each color component. We create a slider for the l component of the color:

var sliderL = sliderControl()
    .domain([0, 100])
    .width(sliderWidth);
    .onSlide(function(selection) {
        selection.each(function(d) {
            data.l = d;
            updateColor(data);
        });
    });

The l component of the color is updated on the slider, and then the updateColor function is invoked, passing the color as the argument. We add a group to display the slider and translate it to the appropriate location. Remember that the data bound to the group is the value that is changed when the user moves the handler:

var gSliderL = svg.selectAll('g.slider-l')
    .data([data.l])
    .enter().append('g')
    .attr('transform', function() {
        var dx = margin + labelWidth,
            dy = margin + vScale(0);
        return 'translate(' + [dx, dy] + ')';
    })
    .call(sliderL);

This will create the first slider. In a similar way, we add a slider for the a and b color components with its corresponding groups. When the user moves the slider, the square and the rectangle in the selector should get updated. This chart is independent of the color picker selector to which we don't have access to the selector rectangle in this scope. We will add a configurable method that will be invoked on color change, so the user of labColorWindow can update other components. The updateColor function will update the color of the square and invoke the onColorChange function:

// Update the color square and invoke onColorChange
function updateColor(color) {
    colorSquare.attr('fill', color);
    divContent.data([color]).call(onColorChange);
}

Note that the color is bound to the color picker window div, and onColorChange receives the selection that contains the window. We need to add a default function and an accessor to configure this function. The default function will be just an empty function. We can now update the color picker selector, more precisely, the onClick method, to create the color picker window as follows:

chart.onClick = function(d) {
    // Select the picker rectangle
    var rect = d3.select(this);

    // Select the color picker div and bind the data...
    if (div.empty()) {
        // Create the Color Picker Content
        var content = labColorPickerWindow()
            .onColorChange(function(selection) {
                selection.each(function(d) {
                    rect.data([d]).attr('fill', d);
                });
            });

        // Create the container div, if it doesn't exist.
        div.enter().append('div')
            .attr('class', 'color-picker')
            // set more attributes....
            .call(content);

        // Bind the data to the rectangle again.
        rect.data([div.datum()]);
    } else {
        // Update the color of the rectangle
        rect.data([div.datum()])
            .attr('fill', function(d) { return d; });

        // Remove the color picker window.
        d3.select('body')
            .selectAll('div.color-picker').remove();
    }
};

We can now select a color using the color picker. There is only one thing missing; the user will want to do something with the color once it has been changed. We will add an onChangeColor function and its corresponding accessor to the color picker window and invoke it at the end of the chart.onClick method. With this function, the user will be able to use the color to change other components:

chart.onClick = function(d) {
    // ...

    // Invoke the user callback.
    onColorChange(color);
};

To use the color picker, we need to attach it to a selection that contains a group and configure the onColorChange function:

// Create the color picker
var picker = labColorPicker()
    .color('#fff)
    .onColorChange(function(d) {
        // Change the background color of the page
        d3.select('body').style('background-color', d);
    });

// Create a group for the color picker and translate it.
var grp = svg.append('g')
    .attr('transform', 'translate(30, 30)')
    .call(picker);

This will change the background color of the example page when the user selects a color. In this section, we have used the slider components to create a color picker. The color picker has two independent components: the selector and the color picker window. The color picker selector creates an instance of the color picker window when the user clicks on it and removes the window container when the user clicks on it again.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset