使用 chartjs 的对数图表中的次要网格线

minor grid lines in log chart with chartjs

提问人:punkish 提问时间:7/5/2023 最后编辑:punkish 更新时间:9/11/2023 访问量:276

问:

更新:在线添加了一个工作小提琴,添加了一个工作代码片段:)

我正在从 转向 但坚持尝试在对数刻度图表中显示次要网格线。图表如下图所示(没有次要网格线)。我希望它有像第二张图片中那样的网格线。多次阅读文档没有给我答案。dygraphschartsjschartjs

with chartjs with dygraphs

const termFreqWithChartjs = (ctx, series, term, termFreq) => {
    
    const config = {
        type: 'line',
        data: {
            labels: termFreq.map(e => e.journalYear),
            datasets: [
                {
                    label: series.y1,
                    data: termFreq.map(e => e.total),
                    borderColor: 'red',
                    borderWidth: 1,
                    backgroundColor: 'rgba(255, 0, 0, 0.1)',
                    pointStyle: 'circle',
                    pointRadius: 3,
                    pointBorderColor: 'rgb(0, 0, 0)'
                },
                {
                    label: series.y2,
                    data: termFreq.map(e => e.withImages),
                    borderColor: 'blue',
                    borderWidth: 1,
                    backgroundColor: 'rgba(0, 0, 255, 0.1)',
                    pointStyle: 'circle',
                    pointRadius: 3,
                    pointBorderColor: 'rgb(0, 0, 0)'
                }
            ]
        },
        options: {
            interaction: {
                intersect: false,
                mode: 'x',
            },
            animation: false,
            responsive: true,
            scales: {
                x: {
                    display: true,

                },
                y: {
                    display: true,
                    type: 'logarithmic',
                    grid: {
                        borderColor: 'grey',
                        tickColor: 'grey'
                    },
                    min: 1,
                    ticks: {
                        callback: function (value, index, values) {
                            if (value === 1000000) return "1M";
                            if (value === 100000) return "100K";
                            if (value === 10000) return "10K";
                            if (value === 1000) return "1K";
                            if (value === 100) return "100";
                            if (value === 10) return "10";
                            if (value === 1) return "1";
                            return null;
                        }
                    }
                }
            },
            plugins: {
                title: {
                    display: true,
                    text: `occurrence of '${term}' in text by year`,
                },
                legend: {
                    display: true,
                    position: 'chartArea',
                    labels: {
                        usePointStyle: true,
                    }
                },
                tooltip: {
                    enabled: true
                }
            }
        }
    };

    let canvas = document.getElementById('termFreq');

    if (canvas) {
        termFreqChart.destroy();
        termFreqChart = new Chart(canvas, config);
    }
    else {
        canvas = document.createElement('canvas');
        canvas.id = "termFreq";
        canvas.width = 500;
        canvas.height = 200;
        ctx.appendChild(canvas);
        termFreqChart = new Chart(canvas, config);
    }
}
const termFreq = [
    {
        "journalYear": 1841,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1846,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1850,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1851,
        "total": 26,
        "withImages": 0
    },
    {
        "journalYear": 1853,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1855,
        "total": 7,
        "withImages": 0
    },
    {
        "journalYear": 1857,
        "total": 27,
        "withImages": 0
    },
    {
        "journalYear": 1859,
        "total": 30,
        "withImages": 0
    },
    {
        "journalYear": 1860,
        "total": 4,
        "withImages": 0
    },
    {
        "journalYear": 1861,
        "total": 9,
        "withImages": 0
    },
    {
        "journalYear": 1862,
        "total": 18,
        "withImages": 0
    },
    {
        "journalYear": 1863,
        "total": 4,
        "withImages": 0
    },
    {
        "journalYear": 1866,
        "total": 12,
        "withImages": 0
    },
    {
        "journalYear": 1877,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1884,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1886,
        "total": 12,
        "withImages": 0
    },
    {
        "journalYear": 1887,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1890,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1893,
        "total": 4,
        "withImages": 0
    },
    {
        "journalYear": 1894,
        "total": 9,
        "withImages": 0
    },
    {
        "journalYear": 1895,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1896,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1902,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1904,
        "total": 14,
        "withImages": 0
    },
    {
        "journalYear": 1905,
        "total": 10,
        "withImages": 0
    },
    {
        "journalYear": 1910,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1912,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1913,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1914,
        "total": 7,
        "withImages": 0
    },
    {
        "journalYear": 1915,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1920,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1922,
        "total": 8,
        "withImages": 0
    },
    {
        "journalYear": 1924,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1926,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1928,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1932,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1949,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1950,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1953,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1955,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1956,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1958,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1959,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1960,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1975,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1979,
        "total": 136,
        "withImages": 0
    },
    {
        "journalYear": 1990,
        "total": 27,
        "withImages": 5
    },
    {
        "journalYear": 1992,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1993,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1997,
        "total": 1,
        "withImages": 1
    },
    {
        "journalYear": 2000,
        "total": 251,
        "withImages": 225
    },
    {
        "journalYear": 2001,
        "total": 14,
        "withImages": 0
    },
    {
        "journalYear": 2003,
        "total": 141,
        "withImages": 139
    },
    {
        "journalYear": 2004,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 2005,
        "total": 62,
        "withImages": 0
    },
    {
        "journalYear": 2006,
        "total": 16,
        "withImages": 12
    },
    {
        "journalYear": 2007,
        "total": 79,
        "withImages": 17
    },
    {
        "journalYear": 2008,
        "total": 42,
        "withImages": 16
    },
    {
        "journalYear": 2009,
        "total": 141,
        "withImages": 60
    },
    {
        "journalYear": 2010,
        "total": 111,
        "withImages": 5
    },
    {
        "journalYear": 2011,
        "total": 62,
        "withImages": 14
    },
    {
        "journalYear": 2012,
        "total": 100,
        "withImages": 25
    },
    {
        "journalYear": 2013,
        "total": 137,
        "withImages": 129
    },
    {
        "journalYear": 2014,
        "total": 54,
        "withImages": 23
    },
    {
        "journalYear": 2015,
        "total": 139,
        "withImages": 73
    },
    {
        "journalYear": 2016,
        "total": 166,
        "withImages": 36
    },
    {
        "journalYear": 2017,
        "total": 87,
        "withImages": 74
    },
    {
        "journalYear": 2018,
        "total": 79,
        "withImages": 49
    },
    {
        "journalYear": 2019,
        "total": 142,
        "withImages": 96
    },
    {
        "journalYear": 2020,
        "total": 84,
        "withImages": 27
    },
    {
        "journalYear": 2021,
        "total": 907,
        "withImages": 386
    },
    {
        "journalYear": 2022,
        "total": 79,
        "withImages": 50
    },
    {
        "journalYear": 2023,
        "total": 3,
        "withImages": 0
    }
];

const series = {
    x: "journal year",
    y1: "total",
    y2: "with images"
}

const ctx = document.getElementById('graphdiv');
termFreqWithChartjs(ctx, series, 'formica', termFreq);
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div id="graphdiv"></div>

JavaScript 图表.js dygraphs 网格线

评论

1赞 Douglas Cunha 7/5/2023
您好,您需要发布您的代码,以便我们帮助您识别您的错误。
0赞 kikon 7/6/2023
如果主要价格变动不适合您,您应该使用 grid.lineWidth编写脚本的事实并使用 ticks 回调。或者,按照@DouglasCunha的要求去做,并提供一个工作示例,让我们提供帮助......
0赞 punkish 7/7/2023
我想要刻度之间的网格线,因此,我称之为“次要网格”。特别是在对数图表中,这些次要的网格线(如我发布的 dygraphs 图像所示)给出了比例的视觉指示。举个可行的例子,我已经发布了到目前为止我所得到的代码。虽然我看到一些关于刻度的可脚本性以设置网格样式的参考,但我首先无法弄清楚如何绘制这些线条。我如何告诉在每条主要网格线(标记的网格线)之间绘制 10 条网格线?chartjs
0赞 kikon 7/7/2023
你已经观察到了,我确认了,本身没有小蜱虫。我告诉过你,你可以用代码来做;这就是 Chart.js 的方式。
1赞 punkish 7/7/2023
哦,在线小提琴是个好主意!谢谢!我以前从未做过。我在原始帖子中添加了一个工作示例。

答:

2赞 kikon 7/7/2023 #1
  1. 默认情况下,Chart.js 将为对数轴生成次要网格线。OP 的代码不显示任何次要网格线,因为该函数返回不合格的刻度;这导致该刻度网格线被移除。ticks.callbacknull

    一个可能的解决方案是将 in 替换为 ;这将保留刻度(和网格线),但不显示任何标签。一个缺点是,即使标签的大小为零,系统也会为其分配一个空间,这可能会影响标记的刻度;人们可能想要禁用 .return nullreturn ''ticks.callbackticks.autoSkip: false

  2. 在那之后,真正的挑战是在视觉上区分次要网格线和主要网格线。解决方案来自这样一个事实,即大多数网格线设置都是可编写脚本的选项,可以分配功能,可用于使结果(选项,例如网格线颜色)适应运行时环境 - 在这种情况下,相应的刻度是次要的还是主要的。

    我在下面的代码中确定了所有相关选项,这些选项可用于在视觉上区分次要网格线和刻度与主要网格线和刻度。您可以只保留其中的一部分,也可以不保留。

  3. 我还为函数中的刻度添加了一个可能的过滤器,更多是该功能的演示。也可以通过实际返回一些 s 来过滤 中的网格线。options.scales.y.afterBuildTicksticks.callbacknullvalue

选项,提取:

const config = {
    type: 'line',
    data: {
        // ............
    },
    options: {
        // ..... other options 
        scales: {
            x: // .....
            y: {
                display: true,
                type: 'logarithmic',

                grid: {
                    tickColor: function(data){ // color of the tick line
                        return data.tick.major ? 'rgb(196, 196, 196)' :
                            'rgba(196, 196, 196, 0)' // minor ticks not visible
                    },
                    lineWidth: function(data){ // the width of grid line
                        return data.tick.major ? 2 : 1
                    },
                    color:function(data){ // the color of the grid line
                        return data.tick.major ? 'rgb(196, 196, 196)' :
                            'rgba(196, 196, 196, 0.5)'
                    }
                },
                border:{
                    dash:function(data){
                        // dash pattern for grid lines
                        // (unexpected position for this option here)
                        return data.tick.major ? null : [5, 1]
                    }
                },
                min: 1,
                afterBuildTicks: function(ax){
                    ax.ticks = ax.ticks.filter(({value})=>{
                        const r = value/ Math.pow(10, Math.floor(Math.log10(value)+1e-5));
                        return Math.abs(r - Math.round(r)) < 1e-5
                    })
                    // this eliminates tick values like 15 or 150 and only keeps
                    // those of the form n*10^m with n, m one digit integers
                    // this might not be necessary
                },
                ticks: {
                    callback: function (value, index, ticks) {
                        if (value === 1000000) return "1M";
                        if (value === 100000) return "100K";
                        if (value === 10000) return "10K";
                        if (value === 1000) return "1K";
                        if (value === 100) return "100";
                        if (value === 10) return "10";
                        if (value === 1) return "1";
                        return '';
                    }
                }
            }
        },
        // ......
    }
}
   

完整运行示例,从 OP 分叉而来,高度增加以使对数次要网格线可见。

const termFreqWithChartjs = (ctx, series, term, termFreq) => {
    const config = {
        type: 'line',
        data: {
            labels: termFreq.map(e => e.journalYear),
            datasets: [
                {
                    label: series.y1,
                    data: termFreq.map(e => e.total),
                    borderColor: 'red',
                    borderWidth: 1,
                    backgroundColor: 'rgba(255, 0, 0, 0.1)',
                    pointStyle: 'circle',
                    pointRadius: 3,
                    pointBorderColor: 'rgb(0, 0, 0)'
                },
                {
                    label: series.y2,
                    data: termFreq.map(e => e.withImages),
                    borderColor: 'blue',
                    borderWidth: 1,
                    backgroundColor: 'rgba(0, 0, 255, 0.1)',
                    pointStyle: 'circle',
                    pointRadius: 3,
                    pointBorderColor: 'rgb(0, 0, 0)'
                }
            ]
        },
        options: {
            interaction: {
                intersect: false,
                mode: 'x',
            },
            animation: false,
            responsive: true,
            scales: {
                x: {
                    display: true,
                },
                y: {
                    display: true,
                    type: 'logarithmic',

                    grid: {
                        tickColor: function(data){
                            return data.tick.major ? 'rgb(196, 196, 196)' :
                                'rgba(196, 196, 196, 0)' // minor ticks not visible
                        },
                        lineWidth: function(data){
                            return data.tick.major ? 2 : 1
                        },
                        color:function(data){
                            return data.tick.major ? 'rgb(196, 196, 196)' :
                                'rgba(196, 196, 196, 0.5)'
                        }
                    },
                    border:{
                        dash:function(data){
                            // dash line for grid lines
                            // (unexpected position for this option here)
                            return data.tick.major ? null : [5, 1]
                        }
                    },
                    min: 1,
                    afterBuildTicks: function(ax){
                        ax.ticks = ax.ticks.filter(({value})=>{
                            const r = value/ Math.pow(10, Math.floor(Math.log10(value)+1e-5));
                            return Math.abs(r - Math.round(r)) < 1e-5
                        })
                        // this eliminates tick values like 15 or 150 and only keeps
                        // those of the form n*10^m with n, m one digit integers
                        // this might not be necessary
                    },
                    ticks: {
                        autoSkip: false,
                        callback: function (value, index, ticks) {
                            if (value === 1000000) return "1M";
                            if (value === 100000) return "100K";
                            if (value === 10000) return "10K";
                            if (value === 1000) return "1K";
                            if (value === 100) return "100";
                            if (value === 10) return "10";
                            if (value === 1) return "1";
                            return '';
                        }
                    }
                }
            },
            plugins: {
                title: {
                    display: true,
                    text: `occurrence of '${term}' in text by year`,
                },
                legend: {
                    display: true,
                    position: 'chartArea',
                    labels: {
                        usePointStyle: true,
                    }
                },
                tooltip: {
                    enabled: true
                }
            }
        }
    };

    let canvas = document.getElementById('termFreq');

    if (canvas) {
        termFreqChart.destroy();
        termFreqChart = new Chart(canvas, config);
    }
    else {
        canvas = document.createElement('canvas');
        canvas.id = "termFreq";
        canvas.width = 960;
        canvas.height = 600;
        ctx.appendChild(canvas);
        termFreqChart = new Chart(canvas, config);
    }
}
const termFreq = [
    {
        "journalYear": 1841,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1846,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1850,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1851,
        "total": 26,
        "withImages": 0
    },
    {
        "journalYear": 1853,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1855,
        "total": 7,
        "withImages": 0
    },
    {
        "journalYear": 1857,
        "total": 27,
        "withImages": 0
    },
    {
        "journalYear": 1859,
        "total": 30,
        "withImages": 0
    },
    {
        "journalYear": 1860,
        "total": 4,
        "withImages": 0
    },
    {
        "journalYear": 1861,
        "total": 9,
        "withImages": 0
    },
    {
        "journalYear": 1862,
        "total": 18,
        "withImages": 0
    },
    {
        "journalYear": 1863,
        "total": 4,
        "withImages": 0
    },
    {
        "journalYear": 1866,
        "total": 12,
        "withImages": 0
    },
    {
        "journalYear": 1877,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1884,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1886,
        "total": 12,
        "withImages": 0
    },
    {
        "journalYear": 1887,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1890,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1893,
        "total": 4,
        "withImages": 0
    },
    {
        "journalYear": 1894,
        "total": 9,
        "withImages": 0
    },
    {
        "journalYear": 1895,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1896,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1902,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1904,
        "total": 14,
        "withImages": 0
    },
    {
        "journalYear": 1905,
        "total": 10,
        "withImages": 0
    },
    {
        "journalYear": 1910,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1912,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1913,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1914,
        "total": 7,
        "withImages": 0
    },
    {
        "journalYear": 1915,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1920,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1922,
        "total": 8,
        "withImages": 0
    },
    {
        "journalYear": 1924,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1926,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1928,
        "total": 5,
        "withImages": 0
    },
    {
        "journalYear": 1932,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1949,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1950,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1953,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1955,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1956,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1958,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1959,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1960,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1975,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 1979,
        "total": 136,
        "withImages": 0
    },
    {
        "journalYear": 1990,
        "total": 27,
        "withImages": 5
    },
    {
        "journalYear": 1992,
        "total": 3,
        "withImages": 0
    },
    {
        "journalYear": 1993,
        "total": 1,
        "withImages": 0
    },
    {
        "journalYear": 1997,
        "total": 1,
        "withImages": 1
    },
    {
        "journalYear": 2000,
        "total": 251,
        "withImages": 225
    },
    {
        "journalYear": 2001,
        "total": 14,
        "withImages": 0
    },
    {
        "journalYear": 2003,
        "total": 141,
        "withImages": 139
    },
    {
        "journalYear": 2004,
        "total": 2,
        "withImages": 0
    },
    {
        "journalYear": 2005,
        "total": 62,
        "withImages": 0
    },
    {
        "journalYear": 2006,
        "total": 16,
        "withImages": 12
    },
    {
        "journalYear": 2007,
        "total": 79,
        "withImages": 17
    },
    {
        "journalYear": 2008,
        "total": 42,
        "withImages": 16
    },
    {
        "journalYear": 2009,
        "total": 141,
        "withImages": 60
    },
    {
        "journalYear": 2010,
        "total": 111,
        "withImages": 5
    },
    {
        "journalYear": 2011,
        "total": 62,
        "withImages": 14
    },
    {
        "journalYear": 2012,
        "total": 100,
        "withImages": 25
    },
    {
        "journalYear": 2013,
        "total": 137,
        "withImages": 129
    },
    {
        "journalYear": 2014,
        "total": 54,
        "withImages": 23
    },
    {
        "journalYear": 2015,
        "total": 139,
        "withImages": 73
    },
    {
        "journalYear": 2016,
        "total": 166,
        "withImages": 36
    },
    {
        "journalYear": 2017,
        "total": 87,
        "withImages": 74
    },
    {
        "journalYear": 2018,
        "total": 79,
        "withImages": 49
    },
    {
        "journalYear": 2019,
        "total": 142,
        "withImages": 96
    },
    {
        "journalYear": 2020,
        "total": 84,
        "withImages": 27
    },
    {
        "journalYear": 2021,
        "total": 907,
        "withImages": 386
    },
    {
        "journalYear": 2022,
        "total": 79,
        "withImages": 50
    },
    {
        "journalYear": 2023,
        "total": 3,
        "withImages": 0
    }
];

const series = {
    x: "journal year",
    y1: "total",
    y2: "with images"
}

const ctx = document.getElementById('graphdiv');
termFreqWithChartjs(ctx, series, 'formica', termFreq);
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div id="graphdiv"></div>

评论

1赞 kikon 7/8/2023
图例实际上位于顶部,但由于图例符号的透明度,您会看到其下方的网格线。您可以将其从 更改为 ,例如,如果您更改为 ,您将不再看到这些行。尽管如此,它看起来仍然不是很好,所以你可能想为整个图例放一个背景,这也不是开箱即用的,但我会尝试用插件来做。此解决方案仅适用于图例在图表顶部的默认定位。data.datasets[].backgroundColorbackgroundColor: 'rgba(0, 0, 255, 1)'
1赞 kikon 7/8/2023
下面是一个 jsFiddle 示例,其中有“双倍”数量的次要网格线(实际上是 18 条而不是 8 条)。我使用常数 控制它。对于两个主要价格变动之间的每个间隔,计算次要价格变动步长乘以前一个主要价格变动的值。设置为具有 8 个小刻度的标准视图(例如,1 到 10 之间的 2、3、4、5、6、7、8、9)options.scales.y.afterBuildTicksminorTickStepFractionminorTickStepFractionminorTickStepFraction = 1
1赞 punkish 7/8/2023
我希望我能投赞成票,并多回答几次,但现在,必须投一个赞成票。非常感谢。奇怪的秘密慢慢地向我揭示chartjs
1赞 kikon 7/9/2023
我说的插件,用于为图例设置背景颜色 - 这里有一个小提琴
1赞 punkish 7/10/2023
谢谢!我稍微修改了一下,所以图例在.您可以看到所有宝贵帮助的最终结果。再次感谢roundRect