297 lines
9.6 KiB
JavaScript
Generated
297 lines
9.6 KiB
JavaScript
Generated
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
(function (global) {
|
|
|
|
var frameInsight = global.frameInsight = {};
|
|
|
|
var DURATION_CHART_DURATION = 3000;
|
|
var DURATION_CHART_PADDING_V = 0.05;
|
|
var DURATION_CHART_PADDING_TOP = 0.05;
|
|
var DURATION_CHART_PADDING_BOTTOM = 0.05;
|
|
var DURATION_CHART_NORMAL_FILL = 'green';
|
|
var DURATION_CHART_SLOW_FILL = 'red';
|
|
var SLOW_THRESHOLD = 50;
|
|
|
|
var settings;
|
|
var dpr = window && Math.max(window.devicePixelRatio || 1, 1) || 1;
|
|
var durationChart = {
|
|
ticks: [],
|
|
tickTypes: [],
|
|
tags: []
|
|
};
|
|
var original = {
|
|
setTimeout: global.setTimeout,
|
|
requestAnimationFrame: global.requestAnimationFrame,
|
|
addEventListener: global.addEventListener
|
|
};
|
|
|
|
// var now = global.performance
|
|
// // performance.now has higer accuracy.
|
|
// ? performance.now.bind(performance)
|
|
// : function () {
|
|
// return +new Date();
|
|
// };
|
|
|
|
// performance.now is not mocked in the visual regression test. Always use Date.now
|
|
var now = function () {
|
|
return Date.now();
|
|
};
|
|
|
|
instrumentBase();
|
|
|
|
/**
|
|
* @public
|
|
* @param {Object} echarts
|
|
* @param {string} durationChartDom
|
|
*/
|
|
frameInsight.init = function (echarts, durationChartDom, dontInstrumentECharts) {
|
|
settings = {
|
|
echarts: echarts,
|
|
durationChartDom: durationChartDom
|
|
};
|
|
|
|
!dontInstrumentECharts && instrumentECharts();
|
|
initDurationChart();
|
|
startDurationChart();
|
|
};
|
|
|
|
function startDurationChart() {
|
|
next();
|
|
|
|
function next() {
|
|
renderDurationChart();
|
|
original.requestAnimationFrame.call(global, next);
|
|
}
|
|
}
|
|
|
|
function instrumentBase() {
|
|
doInstrumentRegistrar('setTimeout', 0);
|
|
doInstrumentRegistrar('requestAnimationFrame', 0);
|
|
doInstrumentRegistrar('addEventListenter', 1);
|
|
}
|
|
|
|
function instrumentECharts() {
|
|
var echarts = settings.echarts;
|
|
|
|
var dummyDom = document.createElement('div');
|
|
var dummyChart = echarts.init(dummyDom, null, {width: 10, height: 10});
|
|
var ECClz = dummyChart.constructor;
|
|
dummyChart.dispose();
|
|
|
|
ECClz.prototype.setOption = doInstrumentHandler(ECClz.prototype.setOption, 'setOption');
|
|
}
|
|
|
|
function doInstrumentRegistrar(name, handlerIndex) {
|
|
global[name] = function () {
|
|
var args = [].slice.call(arguments);
|
|
args[handlerIndex] = doInstrumentHandler(args[handlerIndex], name);
|
|
return original[name].apply(this, args);
|
|
};
|
|
}
|
|
|
|
function doInstrumentHandler(orginalHandler, tag) {
|
|
return function () {
|
|
var start = now();
|
|
var result = orginalHandler.apply(this, arguments);
|
|
var end = now();
|
|
addTick(start, end, tag);
|
|
return result;
|
|
};
|
|
}
|
|
|
|
function addTick(start, end, tag) {
|
|
var ticks = durationChart.ticks;
|
|
var tickTypes = durationChart.tickTypes;
|
|
var tags = durationChart.tags;
|
|
|
|
// Arbitrary number.
|
|
if (end - start > 0.3) {
|
|
|
|
// In case that setOption in event listener.
|
|
var lastIndex = tickTypes.length - 1;
|
|
if (tickTypes[lastIndex] === 0) {
|
|
tickTypes[lastIndex] = end;
|
|
tags[lastIndex] = tag;
|
|
}
|
|
else {
|
|
ticks.push(start, end);
|
|
// 0: start, 1: end
|
|
tickTypes.push(0, 1);
|
|
tag = tag;
|
|
tags.push(tag, tag);
|
|
}
|
|
}
|
|
|
|
if (!ticks.length) {
|
|
return;
|
|
}
|
|
|
|
var newStart = end - DURATION_CHART_DURATION;
|
|
var dropCount = 0;
|
|
for (var i = 0; i < ticks.length; i++) {
|
|
var tick = ticks[i];
|
|
if (tick < newStart) {
|
|
dropCount++;
|
|
}
|
|
}
|
|
if (dropCount > 0) {
|
|
ticks.splice(0, dropCount);
|
|
tickTypes.splice(0, dropCount);
|
|
tags.splice(0, dropCount);
|
|
}
|
|
}
|
|
|
|
function initDurationChart() {
|
|
var dom = document.getElementById(settings.durationChartDom);
|
|
var domStyle = dom.style;
|
|
// domStyle.border = '2px solid #333';
|
|
domStyle.boxShadow = '0 0 3px #000';
|
|
domStyle.backgroundColor = '#eee';
|
|
domStyle.padding = '0';
|
|
domStyle.height = '60px';
|
|
domStyle.margin = '10px 20px';
|
|
|
|
var domWidth = getSize(dom, 0);
|
|
var domHeight = getSize(dom, 1);
|
|
|
|
durationChart.canvas = document.createElement('canvas');
|
|
dom.appendChild(durationChart.canvas);
|
|
durationChart.canvas.style.width = domWidth + 'px';
|
|
durationChart.canvas.style.height = domHeight + 'px';
|
|
durationChart.width = durationChart.canvas.width = domWidth * dpr;
|
|
durationChart.height = durationChart.canvas.height = domHeight * dpr;
|
|
|
|
durationChart.ctx = durationChart.canvas.getContext('2d');
|
|
|
|
var paddingV = durationChart.width * DURATION_CHART_PADDING_V;
|
|
var paddingTop = durationChart.height * DURATION_CHART_PADDING_TOP;
|
|
var paddingBottom = durationChart.height * DURATION_CHART_PADDING_BOTTOM;
|
|
durationChart.bodyLeft = paddingV;
|
|
durationChart.bodyWidth = durationChart.width - 2 * paddingV;
|
|
durationChart.bodyTop = paddingTop;
|
|
durationChart.bodyHeight = durationChart.height - paddingTop - paddingBottom;
|
|
|
|
durationChart.renderExtent = [durationChart.bodyLeft, durationChart.bodyLeft + durationChart.bodyWidth];
|
|
var extent = [0, DURATION_CHART_DURATION];
|
|
durationChart.slowThresholdLength =
|
|
linearMap(SLOW_THRESHOLD, extent, durationChart.renderExtent)
|
|
- linearMap(0, extent, durationChart.renderExtent);
|
|
}
|
|
|
|
function renderDurationChart() {
|
|
// var renderStart = now();
|
|
|
|
var ticks = durationChart.ticks;
|
|
var tickTypes = durationChart.tickTypes;
|
|
var ctx = durationChart.ctx;
|
|
var timeEnd = now();
|
|
var timeExtent = [timeEnd - DURATION_CHART_DURATION, timeEnd];
|
|
var slowThresholdLength = durationChart.slowThresholdLength;
|
|
|
|
ctx.clearRect(0, 0, durationChart.width, durationChart.height);
|
|
|
|
ctx.fillStyle = DURATION_CHART_NORMAL_FILL;
|
|
|
|
var x;
|
|
var slowRects = [];
|
|
|
|
if (tickTypes[0] === 1) {
|
|
x = durationChart.bodyLeft;
|
|
}
|
|
|
|
for (var i = 0; i < ticks.length; i++) {
|
|
var tick = ticks[i];
|
|
var tickType = tickTypes[i];
|
|
|
|
var tickCoord = linearMap(tick, timeExtent, durationChart.renderExtent);
|
|
if (tickType === 0) {
|
|
x = tickCoord;
|
|
}
|
|
else if (tickType === 1) {
|
|
var width = Math.max(tickCoord - x, 0.5);
|
|
ctx.fillRect(x, durationChart.bodyTop, width, durationChart.bodyHeight);
|
|
if (width > slowThresholdLength) {
|
|
slowRects.push(
|
|
x + slowThresholdLength,
|
|
durationChart.bodyTop,
|
|
width - slowThresholdLength,
|
|
durationChart.bodyHeight
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (slowRects.length) {
|
|
for (var i = 0; i < slowRects.length;) {
|
|
var x = slowRects[i++];
|
|
var y = slowRects[i++];
|
|
var width = slowRects[i++];
|
|
var height = slowRects[i++];
|
|
var canvasGradient = ctx.createLinearGradient(x, y, x + width, y);
|
|
canvasGradient.addColorStop(0, DURATION_CHART_NORMAL_FILL);
|
|
canvasGradient.addColorStop(1, DURATION_CHART_SLOW_FILL);
|
|
ctx.fillStyle = canvasGradient;
|
|
ctx.fillRect(x, y, width, height);
|
|
}
|
|
}
|
|
|
|
// var renderDuration = now() - renderStart;
|
|
// if (renderDuration > 1) {
|
|
// console.warn(renderDuration);
|
|
// }
|
|
}
|
|
|
|
function linearMap(val, domain, range) {
|
|
var subDomain = domain[1] - domain[0];
|
|
var subRange = range[1] - range[0];
|
|
|
|
if (val <= domain[0]) {
|
|
return range[0];
|
|
}
|
|
if (val >= domain[1]) {
|
|
return range[1];
|
|
}
|
|
|
|
return (val - domain[0]) / subDomain * subRange + range[0];
|
|
}
|
|
|
|
function getSize(root, whIdx) {
|
|
var wh = ['width', 'height'][whIdx];
|
|
var cwh = ['clientWidth', 'clientHeight'][whIdx];
|
|
var plt = ['paddingLeft', 'paddingTop'][whIdx];
|
|
var prb = ['paddingRight', 'paddingBottom'][whIdx];
|
|
|
|
// IE8 does not support getComputedStyle, but it use VML.
|
|
var stl = document.defaultView.getComputedStyle(root);
|
|
|
|
return (
|
|
(root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
|
|
- (parseInt10(stl[plt]) || 0)
|
|
- (parseInt10(stl[prb]) || 0)
|
|
) | 0;
|
|
}
|
|
|
|
function parseInt10(val) {
|
|
return parseInt(val, 10);
|
|
}
|
|
|
|
})(window);
|