You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/09/13 05:49:48 UTC

[echarts] branch fix-line-gradient created (now 344b648)

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a change to branch fix-line-gradient
in repository https://gitbox.apache.org/repos/asf/echarts.git.


      at 344b648  fix(line): soft clipping gradient.

This branch includes the following new commits:

     new 344b648  fix(line): soft clipping gradient.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 01/01: fix(line): soft clipping gradient.

Posted by sh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch fix-line-gradient
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 344b648c479f648bdbd6c361b4a02715fc49c6b3
Author: pissang <bm...@gmail.com>
AuthorDate: Mon Sep 13 13:48:59 2021 +0800

    fix(line): soft clipping gradient.
---
 src/chart/line/LineView.ts                       |  97 ++++++++++++-----
 src/export/core.ts                               |   2 +-
 test/{linear-gradient.html => line-visual2.html} | 131 ++++++++++++++++++++++-
 test/runTest/actions/__meta__.json               |   1 +
 test/runTest/actions/line-visual2.json           |   1 +
 5 files changed, 205 insertions(+), 27 deletions(-)

diff --git a/src/chart/line/LineView.ts b/src/chart/line/LineView.ts
index 6103b32..fa4b3a9 100644
--- a/src/chart/line/LineView.ts
+++ b/src/chart/line/LineView.ts
@@ -56,6 +56,7 @@ import {getDefaultLabel, getDefaultInterpolatedLabel} from '../helper/labelHelpe
 import { getECData } from '../../util/innerStore';
 import { createFloat32Array } from '../../util/vendor';
 import { convertToColorString } from '../../util/format';
+import { lerp } from 'zrender/src/tool/color';
 
 type PolarArea = ReturnType<Polar['getArea']>;
 type Cartesian2DArea = ReturnType<Cartesian2D['getArea']>;
@@ -191,9 +192,56 @@ function turnPointsIntoStep(
     return stepPoints;
 }
 
+/**
+ * Clip color stops to edge. Avoid creating too large gradients.
+ * Which may lead to blurry when GPU acceleration is enabled. See #15680
+ *
+ * The stops has been sorted from small to large.
+ */
+function clipColorStops(colorStops: ColorStop[], maxSize: number): ColorStop[] {
+    const newColorStops: ColorStop[] = [];
+    const len = colorStops.length;
+    // coord will always < 0 in prevOutOfRangeColorStop.
+    let prevOutOfRangeColorStop: ColorStop;
+    let prevInRangeColorStop: ColorStop;
+
+    function lerpStop(stop0: ColorStop, stop1: ColorStop, clippedCoord: number) {
+        const coord0 = stop0.coord;
+        const p = (clippedCoord - coord0) / (stop1.coord - coord0);
+        const color = lerp(p, [stop0.color, stop1.color]) as string;
+        return { coord: clippedCoord, color } as ColorStop;
+    }
+
+    for (let i = 0; i < len; i++) {
+        const stop = colorStops[i];
+        const coord = stop.coord;
+        if (coord < 0) {
+            prevOutOfRangeColorStop = stop;
+        }
+        else if (coord > maxSize) {
+            if (prevInRangeColorStop) {
+                newColorStops.push(lerpStop(prevInRangeColorStop, stop, maxSize));
+            }
+            // All following stop will be out of range. So just ignore them.
+            break;
+        }
+        else {
+            if (prevOutOfRangeColorStop) {
+                newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop, 0));
+                // Reset
+                prevOutOfRangeColorStop = null;
+            }
+            newColorStops.push(stop);
+            prevInRangeColorStop = stop;
+        }
+    }
+    return newColorStops;
+}
+
 function getVisualGradient(
     data: SeriesData,
-    coordSys: Cartesian2D | Polar
+    coordSys: Cartesian2D | Polar,
+    api: ExtensionAPI
 ) {
     const visualMetaList = data.getVisual('visualMeta');
     if (!visualMetaList || !visualMetaList.length || !data.count()) {
@@ -236,19 +284,14 @@ function getVisualGradient(
     // LinearGradient to render `outerColors`.
 
     const axis = coordSys.getAxis(coordDim);
-    const axisScaleExtent = axis.scale.getExtent();
 
     // dataToCoord mapping may not be linear, but must be monotonic.
     const colorStops: ColorStop[] = zrUtil.map(visualMeta.stops, function (stop) {
-        let coord = axis.toGlobalCoord(axis.dataToCoord(stop.value));
-        // normalize the infinite value
-        isNaN(coord) || isFinite(coord)
-            || (coord = axis.toGlobalCoord(axis.dataToCoord(axisScaleExtent[+(coord < 0)])));
+        // offset will be calculated later.
         return {
-            offset: 0,
-            coord,
+            coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
             color: stop.color
-        };
+        } as ColorStop;
     });
     const stopLen = colorStops.length;
     const outerColors = visualMeta.outerColors.slice();
@@ -257,34 +300,40 @@ function getVisualGradient(
         colorStops.reverse();
         outerColors.reverse();
     }
+    const colorStopsInRange = clipColorStops(
+        colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight()
+    );
+    const inRangeStopLen = colorStopsInRange.length;
+    if (!inRangeStopLen && stopLen) {
+        // All stops are out of range. All will be the same color.
+        return colorStops[0].coord < 0
+            ? (outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color)
+            : (outerColors[0] ? outerColors[0] : colorStops[0].color);
+    }
 
-    const tinyExtent = 10; // Arbitrary value: 10px
-    const minCoord = colorStops[0].coord - tinyExtent;
-    const maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
+    const tinyExtent = 0; // Arbitrary value: 10px
+    const minCoord = colorStopsInRange[0].coord - tinyExtent;
+    const maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent;
     const coordSpan = maxCoord - minCoord;
 
     if (coordSpan < 1e-3) {
         return 'transparent';
     }
 
-    zrUtil.each(colorStops, function (stop) {
+    zrUtil.each(colorStopsInRange, function (stop) {
         stop.offset = (stop.coord - minCoord) / coordSpan;
     });
-    colorStops.push({
-        offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
+    colorStopsInRange.push({
+        // NOTE: inRangeStopLen may still be 0 if stoplen is zero.
+        offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5,
         color: outerColors[1] || 'transparent'
     });
-    colorStops.unshift({ // notice colorStops.length have been changed.
-        offset: stopLen ? colorStops[0].offset : 0.5,
+    colorStopsInRange.unshift({ // notice newColorStops.length have been changed.
+        offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5,
         color: outerColors[0] || 'transparent'
     });
 
-    // zrUtil.each(colorStops, function (colorStop) {
-    //     // Make sure each offset has rounded px to avoid not sharp edge
-    //     colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
-    // });
-
-    const gradient = new graphic.LinearGradient(0, 0, 0, 0, colorStops, true);
+    const gradient = new graphic.LinearGradient(0, 0, 0, 0, colorStopsInRange, true);
     gradient[coordDim] = minCoord;
     gradient[coordDim + '2' as 'x2' | 'y2'] = maxCoord;
 
@@ -626,7 +675,7 @@ class LineView extends ChartView {
             }
         }
         this._clipShapeForSymbol = clipShapeForSymbol;
-        const visualColor = getVisualGradient(data, coordSys)
+        const visualColor = getVisualGradient(data, coordSys, api)
             || data.getVisual('style')[data.getVisual('drawType')];
         // Initialization animation or coordinate system changed
         if (
diff --git a/src/export/core.ts b/src/export/core.ts
index c1702f2..d17b784 100644
--- a/src/export/core.ts
+++ b/src/export/core.ts
@@ -24,7 +24,7 @@ export * from './api';
 import { use } from '../extension';
 
 // Import label layout by default.
-// TODO remove
+// TODO will be treeshaked.
 import {installLabelLayout} from '../label/installLabelLayout';
 use(installLabelLayout);
 
diff --git a/test/linear-gradient.html b/test/line-visual2.html
similarity index 55%
rename from test/linear-gradient.html
rename to test/line-visual2.html
index 5ec8200..44f2b9b 100644
--- a/test/linear-gradient.html
+++ b/test/line-visual2.html
@@ -40,6 +40,7 @@ under the License.
         <div id="main0"></div>
         <div id="main1"></div>
         <div id="main2"></div>
+        <div id="main3"></div>
 
 
 
@@ -151,8 +152,7 @@ under the License.
                         'The series with log axis should be rendered without errors in console',
                         'https://github.com/apache/echarts/issues/14601'
                     ],
-                    option: option,
-                    height: 600
+                    option: option
                 });
 
             })
@@ -367,6 +367,133 @@ under the License.
         </script>
 
 
+        <script>
+            require([
+                'echarts'
+            ], function (echarts) {
+                var option = {
+                    title: {
+                        text: 'Awesome Chart'
+                    },
+                    xAxis: {
+                        data: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
+                    },
+                    yAxis: {},
+                    series: [{
+                        type: 'line',
+                        data:[220, 182, 191, 234, 290, 330, 310]
+                    }]
+                };
 
+                var $x = ["2020-08-20","2020-08-21","2020-08-24","2020-08-25","2020-08-26","2020-08-27","2020-08-28","2020-08-31","2020-09-01","2020-09-02","2020-09-03","2020-09-04","2020-09-07","2020-09-08","2020-09-09","2020-09-10","2020-09-11","2020-09-14","2020-09-15","2020-09-16","2020-09-17","2020-09-18","2020-09-21","2020-09-22","2020-09-23","2020-09-24","2020-09-25","2020-09-28","2020-09-29","2020-09-30","2020-10-09","2020-10-12","2020-10-13","2020-10-14","2020-10-15","2020-10-16 [...]
+                var $s1 = [-80.65,-86.61,-41.83,-39.31,-100.97,-58.66,-26.04,-32.31,14.12,39.83,19.21,-16.46,-61.23,-44.61,-83.63,-108.42,-87.37,-16.75,86.01,127.74,138.77,167.67,129.31,57.86,9.14,-75.93,-65.58,-64.7,20.89,39.63,97.85,122.55,145.86,105.62,72.92,17.18,-5.71,13.87,7.41,3.13,3.08,7.94,79.47,113.11,107.02,38.1,70.76,88.18,120.21,177.45,218.19,218.29,109.89,36.9,15.74,12.81,75.08,67.41,-34.81,-36.21,5.89,101.44,142.1,87.73,67.8,2.28,-16.82,-27.88,-66.38,-71.15,-12.86,37.25,83 [...]
+                var $s2 = [-55.49,-68.41,-61.64,-62.43,-90,-87.95,-79.76,-78.58,-56.39,-34.33,-27.7,-30.23,-44.81,-42.91,-57.64,-78.61,-87.8,-72.7,-31.45,7.92,41.01,83.05,104.38,102.8,94.82,57.47,38.31,16.42,25.8,26.09,46.42,65.78,95.32,105.37,110.95,97.78,84.58,82.19,70.97,57.39,44.26,34.51,56.02,80.42,93.87,79.69,92.03,106.32,131.8,170.17,207.85,231.99,210.5,181.18,161.55,147.01,155.14,143.67,88.45,56.76,50.55,81.03,112.24,106.37,98.84,74.3,59.59,49.39,24.13,-1.54,-2.91,4.93,25.82,29.1 [...]
+                var $visualMap1 = [{"gt":1,"lte":3,"color":"#ff0000"},{"gt":4,"lte":6,"color":"#ff0000"},{"gt":7,"lte":9,"color":"#ff0000"},{"gt":12,"lte":13,"color":"#ff0000"},{"gt":15,"lte":21,"color":"#ff0000"},{"gt":25,"lte":32,"color":"#ff0000"},{"gt":36,"lte":37,"color":"#ff0000"},{"gt":40,"lte":43,"color":"#ff0000"},{"gt":45,"lte":51,"color":"#ff0000"},{"gt":55,"lte":56,"color":"#ff0000"},{"gt":59,"lte":62,"color":"#ff0000"},{"gt":69,"lte":72,"color":"#ff0000"},{"gt":75,"lte":77," [...]
+                var $visualMap2 = [{"gt":1,"lte":2,"color":"#ff0000"},{"gt":4,"lte":10,"color":"#ff0000"},{"gt":12,"lte":13,"color":"#ff0000"},{"gt":16,"lte":22,"color":"#ff0000"},{"gt":27,"lte":34,"color":"#ff0000"},{"gt":41,"lte":44,"color":"#ff0000"},{"gt":45,"lte":51,"color":"#ff0000"},{"gt":55,"lte":56,"color":"#ff0000"},{"gt":60,"lte":62,"color":"#ff0000"},{"gt":70,"lte":73,"color":"#ff0000"},{"gt":75,"lte":86,"color":"#ff0000"},{"gt":89,"lte":93,"color":"#ff0000"},{"gt":95,"lte":9 [...]
+
+                option = {
+                    dataZoom: [
+                    {
+                        show: true,
+                        type: 'slider',
+                        top: '90%',
+                        start: 85,
+                        end: 100
+                    }
+                    ],
+                    xAxis: {
+                        type: 'category',
+                        boundaryGap: false,
+                        data: $x
+                    },
+                    yAxis: {
+                        type: 'value',
+                        axisLabel: {
+                            formatter: '{value} W'
+                        },
+                        axisPointer: {
+                            snap: true
+                        }
+                    },
+                    visualMap: [{
+                        show: false,
+                        seriesIndex: 0, // $s1
+                        dimension: 0,
+                        pieces: $visualMap1,
+                        outOfRange: {
+                            color: 'green'
+                        },
+                    }, {
+                        show: false,
+                        seriesIndex: 1, // $s2
+                        dimension: 0,
+                        pieces: $visualMap2, //pieces的值由动态数据决定
+                        outOfRange: {
+                            color: 'green'
+                        },
+                    }],
+                    series: [
+                        {
+                            name: '用电量1',
+                            type: 'line',
+                            smooth: true,
+                            data: $s1,
+                            symbol: "none",
+
+                        },{
+                            name: '用电量2',
+                            type: 'line',
+                            smooth: true,
+                            data: $s2,
+                            lineStyle: {
+                                opacity: 1,
+                                width: 1.5,
+                                type: 'dotted'  // 这个是虚线 dashed, dotted更密集
+                            },
+                            symbol: "none",
+                        }
+                    ]
+                };
+                const myChart = testHelper.create(echarts, 'main3', {
+                    title: [
+                        'Graident should have sharp stop on line chart in Chrome with GPU Acceleration enabled.',
+                        'https://github.com/apache/echarts/issues/15680'
+                    ],
+                    button: [{
+                        text: 'zoomToLast',
+                        onclick() {
+                            myChart.setOption({
+                                dataZoom: {
+                                    start: 99,
+                                    end: 100
+                                }
+                            })
+                        }
+                    }, {
+                        text: 'zoomToFirst',
+                        onclick() {
+                            myChart.setOption({
+                                dataZoom: {
+                                    start: 0,
+                                    end: 1
+                                }
+                            })
+                        }
+                    }, {
+                        text: 'zoomToAll',
+                        onclick() {
+                            myChart.setOption({
+                                dataZoom: {
+                                    start: 0,
+                                    end: 100
+                                }
+                            })
+                        }
+                    }],
+                    option: option
+                });
+            })
+
+        </script>
     </body>
 </html>
diff --git a/test/runTest/actions/__meta__.json b/test/runTest/actions/__meta__.json
index 28341e7..54321c5 100644
--- a/test/runTest/actions/__meta__.json
+++ b/test/runTest/actions/__meta__.json
@@ -114,6 +114,7 @@
   "line-crash": 1,
   "line-endLabel": 1,
   "line-extraneous": 2,
+  "line-visual2": 1,
   "lines-bus": 1,
   "map": 3,
   "map-contour": 2,
diff --git a/test/runTest/actions/line-visual2.json b/test/runTest/actions/line-visual2.json
new file mode 100644
index 0000000..f5286b3
--- /dev/null
+++ b/test/runTest/actions/line-visual2.json
@@ -0,0 +1 @@
+[{"name":"Action 1","ops":[{"type":"mousedown","time":425,"x":45,"y":185},{"type":"mouseup","time":524,"x":45,"y":185},{"time":525,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":737,"x":45,"y":185},{"type":"mousemove","time":937,"x":112,"y":182},{"type":"mousemove","time":1174,"x":116,"y":182},{"type":"mousedown","time":1709,"x":116,"y":182},{"type":"mouseup","time":1770,"x":116,"y":182},{"time":1771,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":2054, [...]
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org