Public
Edited
May 2
Insert cell
Insert cell
Insert cell
circlesData = [
{ x: 50, y: 50, color: 'red' },
{ x: 100, y: 100, color: 'blue' },
{ x: 150, y: 150, color: 'green' },
]
Insert cell
Insert cell
{
// crear elemento SVG externo con tamaño 200x200
const width = 200;
const height = 200;
const radius = 10;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
// agregar un fondo
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
// crear un círculo por cada elemento en circlesData
// y definir su posición, color y tamaño
const circles = svg.selectAll('circle')
// copiar los datos para no modificar los objetos originales
.data(circlesData.map(d => ({...d})))
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('fill', d => d.color)
.attr('r', radius);
// nuevo código para arrastrar
// cuando comienza el arrastre
function onDragStart(event, d) {
d3.select(this)
// hacer que el círculo arrastrado aparezca encima
// de otros círculos sobre los que se mueve
.raise()
.attr('fill', 'yellow')
.attr('stroke', 'black');
}
// mientras el arrastre está en progreso
function onDrag(event, d) {
d.x = event.x;
d3.select(this)
.attr('cx', d.x)
.attr('cy', d.y = event.y);
}
// cuando termina el arrastre
function onDragEnd(event, d) {
d3.select(this)
.attr('fill', d.color)
.attr('stroke', 'none');
}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
circles.call(drag);
return svg.node();
}

Insert cell
Insert cell
{
const width = 200;
const height = 200;
const circleRadius = 10;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
const circles = svg.selectAll('circle')
.data(circlesData.map(d => ({...d})))
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('fill', d => d.color)
.attr('r', circleRadius);
function onDragStart(event, d) {
d3.select(this)
.raise()
.attr('fill', 'yellow')
.attr('stroke', 'black');
}
function onDrag(event, d) {
d3.select(this)
.attr('cx', event.x)
.attr('cy', event.y);
}
function onDragEnd(event, d) {
d3.select(this)
.attr('fill', d.color)
.attr('stroke', 'none');
}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
circles.call(drag);
return svg.node();
}

Insert cell
Insert cell
{
const width = 200;
const height = 200;
const radius = 10;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
const circles = svg.selectAll('circle')
.data(circlesData)
.join('circle')
.attr('cx', d => Math.max(radius, Math.min(width - radius, d.x)))
.attr('cy', d => Math.max(radius, Math.min(height - radius, d.y)))
.attr('fill', d => d.color)
.attr('r', radius);

circles.call(circleDrag(width, height, radius));
return svg.node();
}
Insert cell
function circleDrag(width, height, radius) {
function onDragStart(event, d) {
d3.select(this)
.raise()
.attr('fill', 'yellow')
.attr('stroke', 'black');
}
function onDrag(event, d) {
d3.select(this)
.attr('cx', d.x = Math.max(radius, Math.min(width - radius, event.x)))
.attr('cy', d.y = Math.max(radius, Math.min(height - radius, event.y)));
}

function onDragEnd(event, d) {

d3.select(this)
.attr('fill', d.color)
.attr('stroke', 'none');
}
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
return drag;
}
Insert cell
Insert cell
{
const width = 200;
const height = 200;
const radius = 10;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
// Crear un grupo para cada círculo y su tooltip
const circleGroups = svg.selectAll('g')
.data(circlesData)
.join('g');
// Agregar círculos dentro de cada grupo
circleGroups.append('circle')
.attr('cx', d => Math.max(radius, Math.min(width - radius, d.x)))
.attr('cy', d => Math.max(radius, Math.min(height - radius, d.y)))
.attr('fill', d => d.color)
.attr('r', radius);
// Agregar el tooltip (texto) dentro de cada grupo
// Inicialmente oculto (opacidad 0)
circleGroups.append('text')
.attr('class', 'tooltip')
.attr('font-size', '10px')
.attr('text-anchor', 'middle')
.attr('opacity', 0);
// Aplicar el comportamiento de arrastre a los círculos
circleGroups.call(circleDragv2(width, height, radius));
return svg.node();
}
Insert cell

function circleDragv2(width, height, radius) {
function onDragStart(event, d) {
const circle = d3.select(this).select('circle');
const tooltip = d3.select(this).select('text');
circle
.raise()
.attr('fill', 'yellow')
.attr('stroke', 'black');
// Mostrar el tooltip al comenzar el arrastre
tooltip
.attr('opacity', 1)
.attr('x', d.x)
.attr('y', d.y - radius - 5) // Posicionar el tooltip arriba del círculo
.text(`(${Math.round(d.x)}, ${Math.round(d.y)})`);
}
function onDrag(event, d) {
// Actualizar la posición del círculo con límites
d.x = Math.max(radius, Math.min(width - radius, event.x));
d.y = Math.max(radius, Math.min(height - radius, event.y));
const circle = d3.select(this).select('circle');
const tooltip = d3.select(this).select('text');
// Actualizar posición del círculo
circle
.attr('cx', d.x)
.attr('cy', d.y);
// Actualizar posición y texto del tooltip
tooltip
.attr('x', d.x)
.attr('y', d.y - radius - 5)
.text(`(${Math.round(d.x)}, ${Math.round(d.y)})`);
}
function onDragEnd(event, d) {
const circle = d3.select(this).select('circle');
const tooltip = d3.select(this).select('text');
// Restaurar el círculo a su estado original
circle
.attr('fill', d.color)
.attr('stroke', 'none');
// Ocultar el tooltip
tooltip.attr('opacity', 0);
}
// Aplicar el comportamiento de arrastre al grupo completo
const drag = d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd);
return drag;
}
Insert cell
{
const width = 200;
const height = 200;
const radius = 10;
// Datos de ejemplo para los círculos si no existen
const circlesData = window.circlesData || [
{ x: 50, y: 50, color: 'red' },
{ x: 100, y: 100, color: 'blue' },
{ x: 150, y: 150, color: 'green' }
];
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
svg.append('rect')
.attr('width', 200)
.attr('height', 200)
.attr('fill', '#F2ECF3');
// Crear un grupo para cada círculo y su tooltip
const circleGroups = svg.selectAll('g')
.data(circlesData)
.join('g');
// Agregar círculos dentro de cada grupo
circleGroups.append('circle')
.attr('cx', d => Math.max(radius, Math.min(width - radius, d.x)))
.attr('cy', d => Math.max(radius, Math.min(height - radius, d.y)))
.attr('fill', d => d.color)
.attr('r', radius);
// Agregar el tooltip (texto) dentro de cada grupo
// Inicialmente oculto (opacidad 0)
circleGroups.append('text')
.attr('class', 'tooltip')
.attr('font-size', '10px')
.attr('text-anchor', 'middle')
.attr('opacity', 0);
// Aplicar el comportamiento de arrastre a los círculos
circleGroups.call(circleDragv2(width, height, radius));
return svg.node();
}


Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more