[Introduction to d3.js] Introduction and practice of d3-drag

The effect is as shown in the picture, this time it is directly put into actual combat.

 

Use d3-drag to implement interactive drag and drop function

Interaction is a very important part of data visualization, allowing users to interact with charts and perform customized operations. One of them is the drag and drop function, which allows users to freely move elements and change their position or order. In D3.js, we can use d3-drag to implement such interactive drag and drop functionality.

1. Introduce d3-drag library

First, we need to introduce d3.js and d3-drag libraries into the HTML file. It can be introduced through CDN or local files, as shown below:

2. Create draggable elements

Next, we need to create a draggable element. In this example, we will create a simple rectangle and add drag functionality. First, create an SVG container in HTML:

html复制代码<svg id="container" width="400" height="300"></svg>

Then, use D3.js in JavaScript code to create a rectangular element and add drag functionality:

javascript复制代码// 创建一个SVG容器
const svg = d3.select("#container");

// 创建矩形元素
const rect = svg.append("rect")
    .attr("x", 100)
    .attr("y", 100)
    .attr("width", 50)
    .attr("height", 50)
    .attr("fill", "blue");

// 创建拖拽行为
const drag = d3.drag()
    .on("start", dragStarted)
    .on("drag", dragged)
    .on("end", dragEnded);

// 将拖拽行为应用到矩形元素上
rect.call(drag);

// 拖拽开始时的回调函数
function dragStarted(event, d) {
    d3.select(this).raise().attr("fill", "red");
}

// 拖拽过程中的回调函数
function dragged(event, d) {
    d3.select(this)
        .attr("x", event.x)
        .attr("y", event.y);
}

// 拖拽结束时的回调函数
function dragEnded(event, d) {
    d3.select(this).attr("fill", "blue");
}

In the above code, we first select the SVG container using d3.select() and create a rectangular element using the append() method. We then create a drag behavior and apply it to the rectangle element by calling rect.call(drag).

In the callback function of the dragging behavior, we can define the operations at the start of dragging, the dragging process and the end of dragging. When dragging begins, we bring the selected element to the front and change its fill color. During the dragging process, we use event.x and event.y to update the position of the element. At the end of the drag, we return the fill color to its original value.


We have mastered the basic usage of drag, now let's develop a color matching game! Game ideas

  1. Prepare 5 colors and corresponding Chinese names
  2. Draw 5 rectangular areas in black and fill them with the names of the colors.
  3. Draw 5 color blocks and 5 names corresponding to them.
  4. When moving the color block close to the background area, a prompt will appear when the color matches (the color border changes color). Release the mouse and it will automatically snap to the center of the corresponding color block.
  5. When all color blocks are moved to the correct position, the prompt completes all color matching.

This is an interesting little game, suitable for children to play. Various similar games can be developed by transforming one, such as animal recognition. Character recognition, shape recognition. hehe

The code is as follows, read it yourself, I am too lazy to explain.

javascript复制代码// drag
let dom = document.createElement('div');
document.body.appendChild(dom);

let padding = 30;
let svgWidth = 600;
let svgHeight = 300;

let svg = d3.select(dom)
    .append("svg")
    .attr("width", svgWidth)
    .attr("height", svgHeight)
    .style('border', '1px solid #999999')

// 定义5个颜色
let colors = [
    {
        id: 0,
        name: '橙色',
        color: '#ff6633'
    },
    {
        id: 1,
        name: '蓝色',
        color: '#3399ff'
    },
    {
        id: 2,
        name: '绿色',
        color: '#33cc33'
    },
    {
        id: 3,
        name: '紫色',
        color: '#cc33ff'
    },
    {
        id: 4,
        name: '黄色',
        color: '#ffcc00'
    }
]

// 绘制5个矩形
let rectWidth = 100;
let rectHeight = 100;
let rectPadding = 10;

let pos = {}

// 绘制5个矩形背景边框
let gback = svg.append('g').attr('class', 'back');
let rectBacks = gback.selectAll('rect')
    .data(colors)
    .enter()
    .append('rect')
    .attr('x', (d, i) => {
        let x = padding + (rectWidth + rectPadding) * i;
        pos[d.id] = [x, padding]
        return x;
    })
    .attr('cid', d => d.name)
    .attr('ok', 'fail')
    .attr('y', padding)
    .attr('width', rectWidth)
    .attr('height', rectHeight)
    .attr('fill', 'none')
    .attr('stroke', '#000000')
    .attr('stroke-width', '2px')

let _colors = JSON.parse(JSON.stringify(colors));
// 打乱顺序
_colors.sort(function () {
    return Math.random() - 0.5;
})

let grect = svg.append('g').attr('class', 'rect');
let rects = grect.selectAll('rect')
    .data(_colors)
    .enter()
    .append('rect')
    .attr('cid', d => d.name)
    .attr('x', (d, i) => {
        return padding + (rectWidth + rectPadding) * i + rectWidth * 0.1;
    })
    .attr('y', (d, i) => {
        return padding + rectHeight + 50;
    })
    .attr('width', rectWidth * 0.8)
    .attr('height', rectHeight * 0.8)
    .attr('fill', d => d.color)

// 在背景矩形上添加文字 颜色名称
let gtext = svg.append('g').attr('class', 'text');
let texts = gtext.selectAll('text')
    .data(colors)
    .enter()
    .append('text')
    .attr('x', (d, i) => {
        return padding + (rectWidth + rectPadding) * i + rectWidth / 2;
    })
    .attr('y', (d, i) => {
        return padding + rectHeight / 2;
    })
    .attr('text-anchor', 'middle')
    .attr('dominant-baseline', 'middle')
    .attr('fill', '#000000')
    .attr('font-size', '20px')
    .text(d => d.name)
    .style('pointer-events', 'none');

// 提示文本 干得漂亮,你完成了所有的颜色匹配
let infoText = svg
    .append('text')
    .attr('x', svgWidth / 2)
    .attr('y', svgHeight - 80)
    .attr('text-anchor', 'middle')
    .attr('dominant-baseline', 'middle')
    .attr('fill', '#000000')
    .attr('font-size', '24px')
    .text('干得漂亮Tom,你完成了所有的颜色匹配!')
    .style('pointer-events', 'none')
    .style('opacity', 0);
let drag = d3.drag()
    .on('start', function (d, i) {
        d3.select(this)
            .raise()// 提升层级 使得当前元素在最上层
            .transition()
            .attr('stroke', '#000000')
            .attr('stroke-width', '2px')
            // 鼠标样式
            .style('cursor', 'move')

    })
    .on('drag', function (d) {
        let dx = d3.event.dx;
        let dy = d3.event.dy;
        let x = d3.select(this).attr('x');
        let y = d3.select(this).attr('y');
        d3.select(this)
            .attr('x', +x + dx)
            .attr('y', +y + dy);
        // 矩形中心点坐标
        let cx = +x + dx + (rectWidth * 0.8) / 2;
        let cy = +y + dy + (rectHeight * 0.8) / 2;
        let cid = d3.select(this).attr('cid');
        dis(d3.select(this), cx, cy, cid);



    })
    .on('end', function (d, i) {
        d3.select(this)
            .transition()
            .attr('stroke', 'none')
            //鼠标样式
            .style('cursor', 'default')

        if (d3.select(this).attr('ok') === 'success') {
            // 吸附到背景矩形上
            let _x = pos[d.id][0] + rectWidth / 2 - rectWidth * 0.8 / 2;
            let _y = pos[d.id][1] + rectHeight / 2 - rectHeight * 0.8 / 2;
            d3.select(this)
                .transition()
                .attr('x', _x)
                .attr('y', _y)
        }
        goodJob();
    })

rects.call(drag);

function dis(rect, x, y, cid) {
    // 计算x y和 rectBacks 中心点的距离 如果小于 30 就 变色
    rectBacks.each(function (d, i) {
        let cx = pos[i][0] + rectWidth / 2;
        let cy = pos[i][1] + rectHeight / 2;
        let _cid = d3.select(this).attr('cid');
        let distance = Math.sqrt(Math.pow(cx - x, 2) + Math.pow(cy - y, 2));
        if (distance < 30 && cid == _cid) {
            d3.select(this)
                .attr('stroke', d.color)
                .attr('ok', 'success')
            rect.attr('ok', 'success')
        } else {
            if (cid != _cid) return;
            d3.select(this)
                .attr('stroke', '#000000')
                .attr('ok', 'fail')
            rect.attr('ok', 'fail')
        }
        if (d3.select(this).attr('ok') != 'success') {
            gogogo = false;
        }
    })

}

function goodJob() {
    let gogogo = true;
    rectBacks.each(function (d, i) {
        if (d3.select(this).attr('ok') === 'fail') {
            gogogo = false;
        }
    })
    if (gogogo) {
        infoText
            .transition()
            .style('opacity', 1)
    } else {
        infoText
            .transition()
            .style('opacity', 0)
    }
}

Online address: scqilin.github.io/d3js/intera…

 

Guess you like

Origin blog.csdn.net/Cipher_Y/article/details/132147249