#javascript #reactjs #svg #d3.js
#javascript #reactjs #svg #d3.js
Вопрос:
У меня проблема с динамическим изменением атрибутов cx и cy в зависимости от размера window.innerHeight / window.innerWidth. Я отправляю компоненту window.innerHeight / window.innerWidth как высоту / ширину, и это выглядит так:
const SomeChart = ({width, height}) => {
const BubbleChartRef = useRef(null)
const InitialData = [{'r':2,'x':2,'y':1},
{'r':4,'x':3,'y':2},
{'r':5,'x':7,'y':10},
{'r':7,'x':5,'y':3},
{'r':3,'x':8,'y':9}]
useEffect(() => {
const svg = d3.select(BubbleChartRef.current)
let yScale = d3.scaleLinear().domain([0, 20]).range([0,height])
let xScale = d3.scaleLinear().domain([0, 20]).range([0,width])
svg.selectAll("circle")
.data(InitialData)
.enter()
.append("circle")
.attr('r', (d)=>d.r)
.attr('cx', (d, i)=>xScale(d.x))
.attr('cy', (d, i)=>yScale(d.y))
.attr('stroke', 'black')
.attr('fill', 'red')
.style('stroke-width', '1px')
}, [width, height])
return <svg ref={BubbleChartRef} className='bubble-chart-svg'/>
}
это класс пузырьковой диаграммы-svg css:
.bubble-chart-svg{
width: 50vw;
height: 50vh;
}
Когда я добавляю console.log(xScale(4))
, я получаю информацию о новой позиции cx
(после изменения размера), но элемент circle в svg не меняется.
Возможно ли изменить положение этих элементов в моем svg после изменения размера окна?
Редактировать
Вот мой компонент, контролирующий размер окна:
const BubbleChart = () => {
const [height, setHeight] = useState(window.innerWidth);
const [width, setWidth] = useState(window.innerHeight);
const updateDimensions = useCallback(() => {
setHeight(window.innerHeight);
setWidth(window.innerWidth);
},[])
useEffect(() => {
window.addEventListener('resize', updateDimensions);
}, []);
useEffect(() => {
updateDimensions();
return () => window.removeEventListener('resize', updateDimensions);
}, [])
return <SomeChart width={width/2} height={height/2} ></SomeChart>
}
Ответ №1:
Круги не обновляются, потому что код внутри useEffect ссылается только на ввод, который не обновляет уже отображенные элементы при многократном запуске кода. Обратите внимание, что в React повторная визуализация не удаляет предыдущий SVG, даже если SVG в JSX пуст. Это означает, что при повторном рендеринге круги все еще находятся в SVG в браузере.
В более ранних версиях D3 это можно было исправить, изучив шаблон кодирования, называемый общим шаблоном обновления, он же шаблон ввода, обновления, выхода. Этот шаблон был нетривиален для понимания, поэтому в январе 2019 года команда D3 обновила API, чтобы упростить этот процесс с помощью нового метода: selection.join. Этот метод доступен с версии d3 версии 5.8.0, а точнее с версии d3-selection версии 4.0.
С помощью selection.join вместо использования:
svg.selectAll("circle")
.data(InitialData)
.enter()
.append("circle")
.attr('r', (d)=>d.r)
.attr('cx', (d, i)=>xScale(d.x))
.attr('cy', (d, i)=>yScale(d.y))
Вы можете написать:
svg.selectAll("circle")
.data(InitialData)
.join("circle")
.attr('r', (d)=>d.r)
.attr('cx', (d, i)=>xScale(d.x))
.attr('cy', (d, i)=>yScale(d.y))
С selection.join, каждый раз, когда выполняется этот код, он будет проверять, что отображаемые элементы синхронизированы с данными и параметрами: если появляется новая точка данных, будет отображаться новый круг («ввод«); Если cx и cy изменяются, круги будут обновляться («обновление«); Если точка данных будет удалена, связанный круг будет удален («выход«). Из-за простоты участники D3 выразили в социальных сетях, что selection.join является предпочтительным способом написания кода D3.
Если вы все еще хотите понять, как работал старый шаблон ввода, обновления, выхода, который полезен для чтения примеров кода из версий до selection.join, вы можете проверить эту учебную тетрадь Майка Бостока.