Title: [189006] trunk/Source/WebInspectorUI
Revision
189006
Author
mattba...@apple.com
Date
2015-08-26 18:09:44 -0700 (Wed, 26 Aug 2015)

Log Message

Web Inspector: Rendering Frames timeline pie chart should use SVG instead of 2D canvas
https://bugs.webkit.org/show_bug.cgi?id=148457

Reviewed by Timothy Hatcher.

* UserInterface/Views/ChartDetailsSectionRow.css:
(.details-section > .content > .group > .row.chart > .chart-content > svg > path.hidden):
(.details-section > .content > .group > .row.chart > .chart-content > svg > path.chart-segment):
(.details-section > .content > .group > .row.chart > .chart-content > svg > path.empty-chart):
New styles for SVG chart elements.

* UserInterface/Views/ChartDetailsSectionRow.js:
(WebInspector.ChartDetailsSectionRow):
Calculate radii and create SVG chart elements.
(WebInspector.ChartDetailsSectionRow.prototype.clearItems):
Remove chart segment path elements.
(WebInspector.ChartDetailsSectionRow.prototype._needsLayout):
(WebInspector.ChartDetailsSectionRow.prototype._updateLayout.createSegmentPathData):
Helper function that creates path data for a single pie chart segment.
(WebInspector.ChartDetailsSectionRow.prototype._updateLayout):
Creates path elements as needed, and updates path data for for non-zero data points.
(WebInspector.ChartDetailsSectionRow.prototype.set innerLabel): Deleted.
(WebInspector.ChartDetailsSectionRow.prototype.set innerRadius): Deleted.
These properties are now set during construction.
(WebInspector.ChartDetailsSectionRow.prototype.updateLayout): Deleted.
Renamed to _updateLayout.

* UserInterface/Views/TimelineSidebarPanel.js:
Chart size and inner radius passed to chart constructor.

Modified Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (189005 => 189006)


--- trunk/Source/WebInspectorUI/ChangeLog	2015-08-27 00:20:14 UTC (rev 189005)
+++ trunk/Source/WebInspectorUI/ChangeLog	2015-08-27 01:09:44 UTC (rev 189006)
@@ -1,3 +1,35 @@
+2015-08-26  Matt Baker  <mattba...@apple.com>
+
+        Web Inspector: Rendering Frames timeline pie chart should use SVG instead of 2D canvas
+        https://bugs.webkit.org/show_bug.cgi?id=148457
+
+        Reviewed by Timothy Hatcher.
+
+        * UserInterface/Views/ChartDetailsSectionRow.css:
+        (.details-section > .content > .group > .row.chart > .chart-content > svg > path.hidden):
+        (.details-section > .content > .group > .row.chart > .chart-content > svg > path.chart-segment):
+        (.details-section > .content > .group > .row.chart > .chart-content > svg > path.empty-chart):
+        New styles for SVG chart elements.
+
+        * UserInterface/Views/ChartDetailsSectionRow.js:
+        (WebInspector.ChartDetailsSectionRow):
+        Calculate radii and create SVG chart elements.
+        (WebInspector.ChartDetailsSectionRow.prototype.clearItems):
+        Remove chart segment path elements.
+        (WebInspector.ChartDetailsSectionRow.prototype._needsLayout):
+        (WebInspector.ChartDetailsSectionRow.prototype._updateLayout.createSegmentPathData):
+        Helper function that creates path data for a single pie chart segment.
+        (WebInspector.ChartDetailsSectionRow.prototype._updateLayout):
+        Creates path elements as needed, and updates path data for for non-zero data points.
+        (WebInspector.ChartDetailsSectionRow.prototype.set innerLabel): Deleted.
+        (WebInspector.ChartDetailsSectionRow.prototype.set innerRadius): Deleted.
+        These properties are now set during construction.
+        (WebInspector.ChartDetailsSectionRow.prototype.updateLayout): Deleted.
+        Renamed to _updateLayout.
+
+        * UserInterface/Views/TimelineSidebarPanel.js:
+        Chart size and inner radius passed to chart constructor.
+
 2015-08-26  Joseph Pecoraro  <pecor...@apple.com>
 
         Web Inspector: Implement tracking of active stylesheets in the frontend

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.css (189005 => 189006)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.css	2015-08-27 00:20:14 UTC (rev 189005)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.css	2015-08-27 01:09:44 UTC (rev 189006)
@@ -42,6 +42,20 @@
     padding: 8px 5px 0px 12px;
 }
 
+.details-section > .content > .group > .row.chart > .chart-content > svg > path.hidden {
+    display: none;
+}
+.details-section > .content > .group > .row.chart > .chart-content > svg > path.chart-segment {
+    stroke-width: 1;
+    stroke: rgba(255, 255, 255, 0.7);
+}
+
+.details-section > .content > .group > .row.chart > .chart-content > svg > path.empty-chart {
+    fill: rgba(0, 0, 0, 0.02);
+    stroke-width: 1;
+    stroke: rgba(0, 0, 0, 0.1);
+}
+
 .details-section > .content > .group > .row.chart > .chart-content > .legend {
     display: table;
     margin-left: 12px;

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.js (189005 => 189006)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.js	2015-08-27 00:20:14 UTC (rev 189005)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.js	2015-08-27 01:09:44 UTC (rev 189006)
@@ -25,23 +25,26 @@
 
 WebInspector.ChartDetailsSectionRow = class ChartDetailsSectionRow extends WebInspector.DetailsSectionRow
 {
-    constructor(delegate)
+    constructor(delegate, chartSize, innerRadiusRatio)
     {
         super(WebInspector.UIString("No Chart Available"));
 
+        innerRadiusRatio = innerRadiusRatio || 0;
+        console.assert(chartSize > 0, chartSize);
+        console.assert(innerRadiusRatio >= 0 && innerRadiusRatio < 1, innerRadiusRatio);
+
         this.element.classList.add("chart");
 
         this._titleElement = document.createElement("div");
         this._titleElement.className = "title";
         this.element.appendChild(this._titleElement);
 
-        var chartContentElement = document.createElement("div");
+        let chartContentElement = document.createElement("div");
         chartContentElement.className = "chart-content";
         this.element.appendChild(chartContentElement);
 
-        this._canvas = document.createElement("canvas");
-        this._canvas.className = "chart";
-        chartContentElement.appendChild(this._canvas);
+        this._chartElement = createSVGElement("svg");
+        chartContentElement.appendChild(this._chartElement);
 
         this._legendElement = document.createElement("div");
         this._legendElement.className = "legend";
@@ -50,10 +53,9 @@
         this._delegate = delegate;
         this._items = new Map;
         this._title = "";
-        this._innerLabel = "";
-        this._innerRadius = 0;
-        this._innerLabelFontSize = 11;
-        this._shadowColor = "rgba(0, 0, 0, 0.6)";
+        this._chartSize = chartSize;
+        this._radius = (this._chartSize / 2) - 1;   // Subtract one to accomodate chart stroke width.
+        this._innerRadius = innerRadiusRatio ? Math.floor(this._radius * innerRadiusRatio) : 0;
         this._total = 0;
 
         this._svgFiltersElement = document.createElement("svg");
@@ -63,10 +65,42 @@
         this._checkboxStyleElement = document.createElement("style");
         this._checkboxStyleElement.id = "checkbox-styles";
         document.getElementsByTagName("head")[0].append(this._checkboxStyleElement);
+
+        function createEmptyChartPathData(c, r1, r2)
+        {
+            const a1 = 0;
+            const a2 = Math.PI * 1.9999;
+            let x1 = c + Math.cos(a1) * r1,
+                y1 = c + Math.sin(a1) * r1,
+                x2 = c + Math.cos(a2) * r1,
+                y2 = c + Math.sin(a2) * r1,
+                x3 = c + Math.cos(a2) * r2,
+                y3 = c + Math.sin(a2) * r2,
+                x4 = c + Math.cos(a1) * r2,
+                y4 = c + Math.sin(a1) * r2;
+            return [
+                "M", x1, y1,                    // Starting position.
+                "A", r1, r1, 0, 1, 1, x2, y2,   // Draw outer arc.
+                "Z",                            // Close path.
+                "M", x3, y3,                    // Starting position.
+                "A", r2, r2, 0, 1, 0, x4, y4,   // Draw inner arc.
+                "Z"                             // Close path.
+            ].join(" ");
+        }
+
+        this._emptyChartPath = createSVGElement("path");
+        this._emptyChartPath.setAttribute("d", createEmptyChartPathData(this._chartSize / 2, this._radius, this._innerRadius));
+        this._emptyChartPath.classList.add("empty-chart");
+        this._chartElement.appendChild(this._emptyChartPath);
     }
 
     // Public
 
+    get chartSize()
+    {
+        return this._chartSize;
+    }
+
     set title(title)
     {
         if (this._title === title)
@@ -76,26 +110,6 @@
         this._titleElement.textContent = title;
     }
 
-    set innerLabel(label)
-    {
-        if (this._innerLabel === label)
-            return;
-
-        this._innerLabel = label;
-
-        this._needsLayout();
-    }
-
-    set innerRadius(radius)
-    {
-        if (this._innerRadius === radius)
-            return;
-
-        this._innerRadius = radius;
-
-        this._needsLayout();
-    }
-
     get total()
     {
         return this._total;
@@ -139,6 +153,12 @@
 
     clearItems()
     {
+        for (let item of this._items.values()) {
+            let path = item[WebInspector.ChartDetailsSectionRow.ChartSegmentPathSymbol];
+            if (path)
+                path.remove();
+        }
+
         this._total = 0;
         this._items.clear();
 
@@ -262,10 +282,10 @@
         if (this._scheduledLayoutUpdateIdentifier)
             return;
 
-        this._scheduledLayoutUpdateIdentifier = requestAnimationFrame(this.updateLayout.bind(this));
+        this._scheduledLayoutUpdateIdentifier = requestAnimationFrame(this._updateLayout.bind(this));
     }
 
-    updateLayout()
+    _updateLayout()
     {
         if (this._scheduledLayoutUpdateIdentifier) {
             cancelAnimationFrame(this._scheduledLayoutUpdateIdentifier);
@@ -274,58 +294,62 @@
 
         this._updateLegend();
 
-        var width = this._canvas.clientWidth * window.devicePixelRatio;
-        var height = this._canvas.clientHeight * window.devicePixelRatio;
-        this._canvas.width = width;
-        this._canvas.height = height;
+        this._chartElement.setAttribute("width", this._chartSize);
+        this._chartElement.setAttribute("height", this._chartSize);
+        this._chartElement.setAttribute("viewbox", "0 0 " + this._chartSize + " " + this._chartSize);
 
-        var context = this._canvas.getContext("2d");
-        context.clearRect(0, 0, width, height);
-
-        var x = Math.floor(width / 2);
-        var y = Math.floor(height / 2);
-        var radius = Math.floor(Math.min(x, y) * 0.96);   // Add a small margin to prevent clipping of the chart shadow.
-        var innerRadius = Math.floor(radius * this._innerRadius);
-        var startAngle = 1.5 * Math.PI;
-        var endAngle = startAngle;
-
-        function drawSlice(x, y, startAngle, endAngle, color)
+        function createSegmentPathData(c, a1, a2, r1, r2)
         {
-            context.beginPath();
-            context.moveTo(x, y);
-            context.arc(x, y, radius, startAngle, endAngle, false);
-            if (innerRadius > 0)
-                context.arc(x, y, innerRadius, endAngle, startAngle, true);
-            context.fillStyle = color;
-            context.fill();
+            const largeArcFlag = ((a2 - a1) % (Math.PI * 2)) > Math.PI ? 1 : 0;
+            let x1 = c + Math.cos(a1) * r1,
+                y1 = c + Math.sin(a1) * r1,
+                x2 = c + Math.cos(a2) * r1,
+                y2 = c + Math.sin(a2) * r1,
+                x3 = c + Math.cos(a2) * r2,
+                y3 = c + Math.sin(a2) * r2,
+                x4 = c + Math.cos(a1) * r2,
+                y4 = c + Math.sin(a1) * r2;
+            return [
+                "M", x1, y1,                                // Starting position.
+                "A", r1, r1, 0, largeArcFlag, 1, x2, y2,    // Draw outer arc.
+                "L", x3, y3,                                // Connect outer and innner arcs.
+                "A", r2, r2, 0, largeArcFlag, 0, x4, y4,    // Draw inner arc.
+                "Z"                                         // Close path.
+            ].join(" ");
         }
 
-        context.save();
-        context.shadowBlur = 2 * window.devicePixelRatio;
-        context.shadowOffsetY = window.devicePixelRatio;
-        context.shadowColor = this._shadowColor;
-        drawSlice(x, y, 0, 2.0 * Math.PI, "rgb(242, 242, 242)");
-        context.restore();
+        const center = this._chartSize / 2;
+        let startAngle = -Math.PI / 2;
+        let endAngle = 0;
+        for (let [id, item] of this._items) {
+            let path = item[WebInspector.ChartDetailsSectionRow.ChartSegmentPathSymbol];
+            if (!path) {
+                path = createSVGElement("path");
+                path.classList.add("chart-segment");
+                path.setAttribute("fill", item.color);
+                this._chartElement.appendChild(path);
 
-        for (let [id, item] of this._items) {
-            if (item.value === 0)
+                item[WebInspector.ChartDetailsSectionRow.ChartSegmentPathSymbol] = path;
+            }
+
+            if (!item.value) {
+                path.classList.add("hidden");
                 continue;
-            endAngle += (item.value / this._total) * 2.0 * Math.PI;
-            drawSlice(x, y, startAngle, endAngle, item.color);
+            }
+
+            const angle = (item.value / this._total) * Math.PI * 2;
+            endAngle = startAngle + angle;
+
+            path.setAttribute("d", createSegmentPathData(center, startAngle, endAngle, this._radius, this._innerRadius));
+            path.classList.remove("hidden");
+
             startAngle = endAngle;
         }
-
-        if (this._innerLabel) {
-            context.font = (this._innerLabelFontSize * window.devicePixelRatio) + "px sans-serif";
-            var metrics = context.measureText(this._innerLabel);
-            var offsetX = centerX - metrics.width / 2;
-            context.fillStyle = "rgb(68, 68, 68)";
-            context.fillText(this._innerLabel, offsetX, centerY);
-        }
     }
 };
 
 WebInspector.ChartDetailsSectionRow.DataItemIdSymbol = Symbol("chart-details-section-row-data-item-id");
+WebInspector.ChartDetailsSectionRow.ChartSegmentPathSymbol = Symbol("chart-details-section-row-chart-segment-path");
 WebInspector.ChartDetailsSectionRow.LegendItemValueElementSymbol = Symbol("chart-details-section-row-legend-item-value-element");
 
 WebInspector.ChartDetailsSectionRow.Event = {

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js (189005 => 189006)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js	2015-08-27 00:20:14 UTC (rev 189005)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js	2015-08-27 01:09:44 UTC (rev 189006)
@@ -93,8 +93,7 @@
             this._chartColors.set(WebInspector.RenderingFrameTimelineRecord.TaskType.Paint, "rgb(152, 188, 77)");
             this._chartColors.set(WebInspector.RenderingFrameTimelineRecord.TaskType.Other, "rgb(221, 221, 221)");
 
-            this._frameSelectionChartRow = new WebInspector.ChartDetailsSectionRow(this);
-            this._frameSelectionChartRow.innerRadius = 0.5;
+            this._frameSelectionChartRow = new WebInspector.ChartDetailsSectionRow(this, 74, 0.5);
             this._frameSelectionChartRow.addEventListener(WebInspector.ChartDetailsSectionRow.Event.LegendItemChecked, this._frameSelectionLegendItemChecked, this);
 
             for (let key in WebInspector.RenderingFrameTimelineRecord.TaskType) {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to