dwp*_*dwp 8 css tooltip bar-chart recharts
问题: 我创建了一个带有自定义工具提示的条形图。现在我需要的是将工具提示放置在栏的顶部而不是图表区域内。就像这张照片一样。
这就是现在的样子。
在这里我提供了我如何组织我的代码。
import React, { Component } from "react";
import {
BarChart,
Tooltip,
Bar,
Legend,
ResponsiveContainer,
Cell
} from "recharts";
import { Card, CardTitle, CardBody } from "reactstrap";
import "./SessionDuration.css";
const colors = ["#26a0a7", "#79d69f", "#f9ec86", "#ec983d"];
const data = [
{
name: "Page A",
pv: 2400,
amt: 2400
},
{
name: "Page B",
pv: 1398,
amt: 2210
},
{
name: "Page C",
pv: 9800,
amt: 2290
},
{
name: "Page D",
pv: 3908,
amt: 2000
}
];
const getTitleOfTooltip = (label) =>{
if (label === 0) {
return "<=5 min";
}
if (label === 1) {
return "5-30 min";
}
if (label === 2) {
return "30-60 min";
}
if (label === 3) {
return ">60";
}
}
const getIntroOfPage = label => {
if (label === 0) {
return "Page A is about men's clothing";
}
if (label === 1) {
return "Page B is about women's dress";
}
if (label === 2) {
return "Page C is about women's bag";
}
if (label === 3) {
return "Page D is about household goods";
}
};
class SessionDuration extends Component {
render() {
return (
<Card className="session-duration-card">
<CardTitle className="session-duration-card-header">
Session Duration
</CardTitle>
<CardBody>
<ResponsiveContainer width="100%" height="100%" aspect={4.0 / 5.5}>
<BarChart
data={data}
margin={{
top: 3,
right: 5,
left: 5,
bottom: 13
}}
barGap={10}
>
<Tooltip
coordinate={{ x: 0, y: 150 }}
content={<CustomTooltip />}
/>
<Bar dataKey="pv" fill="#8884d8">
{data.map((entry, index) => (
<Cell key={`cell-${index + 1}`} fill={colors[index]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</CardBody>
</Card>
);
}
}
export default SessionDuration;
const CustomTooltip = ({ active, payload, label }) => {
if (active) {
return (
<div className="session-duration-tooltip">
<p className="session-duration-tooltip-label">{getTitleOfTooltip(label)}</p>
<p className="session-duration-tooltip-intro">
{getIntroOfPage(label)}
</p>
<p className="session-duration-tooltip-desc">
Anything you want can be displayed here.
</p>
</div>
);
}
return null;
};
Run Code Online (Sandbox Code Playgroud)
我在这里提供我的 CSS 文件。
@media only screen and (min-width: 1024px) {
.session-duration-card {
margin-top: 14.5%;
margin-left: -44%;
width: 190% !important;
border-radius: none !important;
height: 86%
}
.session-duration-card-header {
font-weight: 700;
text-align: left;
padding-left: 6%
}
.session-duration-tooltip {
width: 210%;
}
.session-duration-tooltip-label {
font-weight: 700;
font-size: 11px;
}
}
@media only screen and (min-width: 308px) and (max-width: 1024px) {
.session-duration-card {
margin-top: 5%;
margin-left: 3.2%;
width: 94.5%;
border-radius: none !important;
}
.session-duration-card-header {
font-weight: 700;
text-align: center;
}
}
@media only screen and (min-width: 1666px) {
.session-duration-card {
margin-top: 11%;
margin-left: -13%;
width: 190% !important;
height: 97%;
border-radius: none !important;
}
.session-duration-card-header {
font-weight: 700;
text-align: center;
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试了很多方法来找到问题的解决方案,但无法完成,有人可以帮助我修改我的代码来完成这项工作。非常感谢。
要解决这个问题,需要获取每个栏和工具提示的高度和宽度。
useState获取并保持状态的高度和宽度,还向状态放置额外的布尔值,以在鼠标位于 Bar 之外时进行处理。(因此,为了实现功能,我们添加带有两个参数data和boolean 的对象)。useEffect, 查找.recharts-tooltip-wrapper类,定义 DOM 元素的高度和宽度。.recharts-tooltip-wrapper. (Rechart.js中.recharts-tooltip-wrapper有自己生成的样式,我们需要保留一些静态样式.recharts-tooltip-wrapper并添加我们的)。import React, { useState, useEffect } from 'react';
import { BarChart, Tooltip, Bar, ResponsiveContainer, Cell } from 'recharts';
import { Card, CardTitle, CardBody } from 'reactstrap';
import './SessionDuration.css';
const colors = ['#26a0a7', '#79d69f', '#f9ec86', '#ec983d'];
const data = [
{
name: 'Page A',
pv: 2400,
amt: 2400,
},
{
name: 'Page B',
pv: 1398,
amt: 2210,
},
{
name: 'Page C',
pv: 9800,
amt: 2290,
},
{
name: 'Page D',
pv: 3908,
amt: 2000,
},
];
const getTitleOfTooltip = label => {
if (label === 0) {
return '<=5 min';
}
if (label === 1) {
return '5-30 min';
}
if (label === 2) {
return '30-60 min';
}
if (label === 3) {
return '>60';
}
};
const getIntroOfPage = label => {
if (label === 0) {
return "Page A is about men's clothing";
}
if (label === 1) {
return "Page B is about women's dress";
}
if (label === 2) {
return "Page C is about women's bag";
}
if (label === 3) {
return 'Page D is about household goods';
}
};
export const SessionDuration = () => {
const [position, setPosition] = useState(null);
useEffect(() => {
const tooltip = document.querySelector('.recharts-tooltip-wrapper');
if (!tooltip) return;
// Init tooltip values
const tooltipHeight = tooltip.getBoundingClientRect().height;
const tooltipWidth = tooltip.getBoundingClientRect().width;
const spaceForLittleTriangle = 10;
// Rewrite tooltip styles
tooltip.style = `
transform: translate(${position?.data.x}px, ${position?.data.y}px);
pointer-events: none; position: absolute;
top: -${tooltipHeight + spaceForLittleTriangle}px;
left: -${tooltipWidth / 2 - position?.data.width / 2}px;
opacity: ${position?.show ? '1' : 0};
transition: all 400ms ease 0s;
`;
}, [position]);
return (
<>
<Card className="session-duration-card">
<CardTitle className="session-duration-card-header">
Session Duration
</CardTitle>
<CardBody>
<ResponsiveContainer width="100%" height="100%" aspect={4.0 / 5.5}>
<BarChart
data={data}
margin={{
top: 100, // for tooltip visibility
right: 5,
left: 5,
bottom: 13,
}}
barGap={10}
>
<Tooltip
cursor={false} // hide hover effect 'grey rectangle'
position={{
// Static position
x: position?.data.x ?? 0,
y: position?.data.y ?? 0,
}}
content={<CustomTooltip />}
/>
<Bar
dataKey="pv"
fill="#8884d8"
// Handlers
onMouseMove={data => setPosition({ data: data, show: true })}
onMouseOut={data => setPosition({ data: data, show: false })}
>
{data.map((entry, index) => (
<Cell key={`cell-${index + 1}`} fill={colors[index]} />
))}
</Bar>
</BarChart>
</ResponsiveContainer>
</CardBody>
</Card>
</>
);
};
export default SessionDuration;
const CustomTooltip = ({ active, payload, label }) => {
if (active) {
return (
<div className="session-duration-tooltip">
<p className="session-duration-tooltip-label">
{getTitleOfTooltip(label)}
</p>
<p className="session-duration-tooltip-intro">
{getIntroOfPage(label)}
</p>
<p className="session-duration-tooltip-desc">
Anything you want can be displayed here.
</p>
</div>
);
}
return null;
};Run Code Online (Sandbox Code Playgroud)
:root {
--bg-color: hsl(270 3% 29% / 0.9);
}
.session-duration-tooltip {
max-width: 250px;
position: relative;
padding: 0.5em;
border-radius: 5px;
background-color: var(--bg-color);
color: aliceblue;
}
.session-duration-tooltip::after {
content: '';
height: 0;
width: 0;
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
border-style: solid;
border-width: 10px 10px 0 10px;
border-color: var(--bg-color) transparent transparent transparent;
}Run Code Online (Sandbox Code Playgroud)
很多人在这里要求这个功能https://github.com/recharts/recharts/issues/222和这里https://github.com/recharts/recharts/issues/488。我想出了
\n不幸的是,这个答案的问题是你仍然必须悬停“栏”本身,而我的栏非常窄,所以我希望它显示出来,无论你悬停在“栏”还是“光标区域”,这个解决方案允许后者。
\n基本上:
\n从完整条形图中获取活动工具提示索引(光标区域而不是特定条形上的更改)
\nuseEffect 钩子使用 bar 类抓取所有栏".recharts-bar-rectangle",并使用 toolTipIndex 抓取当前活动的栏,获取高度,并将其设置为工具提示 Y 位置。
与之前一样,工具提示由内部 x 位置控制,但 y 位置是高度 +/- 一些偏移。
\nexport default function BarChartCard(props) {\n const [activeBarIndex, setActiveBarIndex] = useState();\n const [toolTipYPosition, setToolTipYPosition] = useState({});\n\n\n useEffect(() => {\n const barCharts = document.querySelectorAll(".recharts-bar-rectangle");\n if (!barCharts || !activeBarIndex) return;\n // Init tooltip values\n const barChart = barCharts[activeBarIndex];\n setToolTipYPosition(barChart.getBoundingClientRect().height);\n }, [activeBarIndex]);\n\n return (\n <BaseCard>\n <Grid container spacing={2}>\n <Grid item xs={12}>\n <Typography variant="h2">{props.title}</Typography>\n </Grid>\n </Grid>\n <ResponsiveContainer width={"100%"} height={300}>\n <BarChart\n barGap={0}\n data={props.data}\n margin={{\n top: 5,\n right: 30,\n left: 20,\n bottom: 5,\n }}\n onMouseMove={(e) => {\n setActiveBarIndex(e.activeTooltipIndex);\n }}\n >\n <Tooltip\n content={<CustomTooltip />}\n cursor={false}\n position={{ y: 170 - toolTipYPosition }}\n offset={-60}\n allowEscapeViewBox={{ x: true, y: true }}\n />\n <XAxis\n style={{\n fontSize: "1.125rem",\n fontFamily: "Cairo",\n fontWeight: 600,\n }}\n axisLine={false}\n tickLine={false}\n dataKey="date"\n tickFormatter={ReformatDateShortDayName}\n />\n <YAxis\n allowDecimals={false}\n style={{\n fontSize: "1.125rem",\n fontFamily: "Cairo",\n fontWeight: 600,\n }}\n dataKey="value"\n axisLine={false}\n tickLine={false}\n />\n <Legend\n style={{\n fontSize: "1.125rem",\n fontFamily: "Cairo",\n fontWeight: 600,\n }}\n iconType="circle"\n iconSize={6}\n align="right"\n verticalAlign="top"\n height={36}\n formatter={(value) => (\n <span\n style={{\n color: "#CCC",\n fontSize: "1.125rem",\n fontWeight: 600,\n fontFamily: "Cairo",\n }}\n >\n {value}\n </span>\n )}\n />\n <Bar\n barSize={10}\n dataKey="value"\n name={props.legendText}\n fill="#EFC92B"\n />\n </BarChart>\n </ResponsiveContainer>\n </BaseCard>\n );\n}\n\nRun Code Online (Sandbox Code Playgroud)\n基于其他一些实现和答案:
\nconst [barGraphData, setBarGraphData] = useState({})\n\nreturn (\n<BarChart barGap={0} data={props.data} margin={{\n top: 5,\n right: 30,\n left: 20,\n bottom: 5\n }}>\n <Tooltip content={<CustomTooltip />}\n cursor={{ fill: \'transparent\' }}\n position={{ y: barGraphData.height + 40}}\n offset={-60}\n />\n <XAxis />\n <YAxis />\n <Legend />\n <Bar \n barSize={10} \n dataKey="value" \n fill="#EFC92B" \n onMouseOver={(data)=> {\n setBarGraphData(data)\n }}\n onMouseLeave={(data) => {\n setBarGraphData(data)\n }}\n />\n</BarChart>\n)\nRun Code Online (Sandbox Code Playgroud)\n所以基本上:
\n正如您从我的代码中看到的,我根据 recharts 文档使用了自定义工具提示
\nconst CustomTooltip = ({ active, payload, label }) => {\n const classes = useStyles();\n return (\n <Fragment>\n {active && payload?.length >= 1 &&\n <Card className={classes.toolTip}>\n <CardContent className={classes.toolTipContent}>\n <Typography className={classes.toolTipLabel}>{label}</Typography>\n <Typography className={classes.toolTipValue}>{payload[0].payload.value}</Typography>\n </CardContent>\n </Card>\n }\n </Fragment>\n )\n}\nRun Code Online (Sandbox Code Playgroud)\n但这仅适用于格式/样式,它也应该适用于默认格式/样式。
\n不幸的是,这似乎只有当您将鼠标悬停在实际的“栏”而不是“光标”定义的整个区域时才起作用。
\n这导致我想到了第二个解决方案,不幸的是它给出了警告
\nWarning: Cannot update a component (`BarChartCard`) while rendering a different component (`label`). To locate the bad setState() call inside `label`, follow the stack trace as described in ...\nRun Code Online (Sandbox Code Playgroud)\n创建让它运行的代码就在这里,任何关于如何使这项工作工作的评论都会很棒!
\nexport default function BarChartCard(props) {\n const [barGraphData, setBarGraphData] = useState({});\n const [activeBarIndex, setActiveBarIndex] = useState({});\n const [toolTipYPosition, setToolTipYPosition] = useState({});\n\n return (\n <BaseCard>\n <Grid container spacing={2}>\n <Grid item xs={12}>\n <Typography variant="h2">{props.title}</Typography>\n </Grid>\n </Grid>\n <ResponsiveContainer\n width={"100%"}\n height={300}\n >\n <BarChart\n barGap={0}\n data={props.data}\n margin={{\n top: 5,\n right: 30,\n left: 20,\n bottom: 5,\n }}\n onMouseMove={(e) => {\n setActiveBarIndex(e.activeTooltipIndex)\n }}\n >\n <Tooltip\n content={<CustomTooltip />}\n cursor={false}\n position={{ y: -toolTipYPosition + 170}}\n offset={-60}\n allowEscapeViewBox={{ x: true, y: true }}\n />\n <XAxis\n style={{\n fontSize: "1.125rem",\n fontFamily: "Cairo",\n fontWeight: 600,\n }}\n axisLine={false}\n tickLine={false}\n dataKey="date"\n tickFormatter={ReformatDateShortDayName}\n />\n <YAxis\n allowDecimals={false}\n style={{\n fontSize: "1.125rem",\n fontFamily: "Cairo",\n fontWeight: 600,\n }}\n dataKey="value"\n axisLine={false}\n tickLine={false}\n />\n <Legend\n style={{\n fontSize: "1.125rem",\n fontFamily: "Cairo",\n fontWeight: 600,\n }}\n iconType="circle"\n iconSize={6}\n align="right"\n verticalAlign="top"\n height={36}\n formatter={(value) => (\n <span\n style={{\n color: "#CCC",\n fontSize: "1.125rem",\n fontWeight: 600,\n fontFamily: "Cairo",\n }}\n >\n {value}\n </span>\n )}\n />\n <Bar\n label={(e) => {\n if(e.index === activeBarIndex){\n setToolTipYPosition(e.height)\n }\n return null;\n }}\n barSize={10}\n dataKey="value"\n name={props.legendText}\n fill="#EFC92B"\n onMouseOver={(data) => {\n setBarGraphData(data);\n }}\n onMouseLeave={(data) => {\n setBarGraphData(data);\n }}\n />\n </BarChart>\n </ResponsiveContainer>\n </BaseCard>\n );\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
6903 次 |
| 最近记录: |