Skip to content

Commit

Permalink
feat(options): Intent to ship render option
Browse files Browse the repository at this point in the history
- Implement lazy rendering and observe element's visibility by
MutationObserver
- Add rendering state variable `Chart.internal.rendered` to determine
rendering process status
- Make sure return only when is array type for .convertData()

Fix #1015
  • Loading branch information
netil authored Aug 13, 2019
1 parent 31a5ae2 commit b6af77f
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 43 deletions.
38 changes: 38 additions & 0 deletions demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2985,6 +2985,44 @@ d3.select(".chart_area")
}
}
},
LazyRender: [
{
options: {
data: {
columns: [
["data1", 300, 350, 300, 0, 0, 0],
["data2", 130, 100, 140, 200, 150, 50]
]
}
},
style: [
"#lazyRender_1 { display: none; }"
],
func: function(chart) {
setTimeout(function() {
document.getElementById("lazyRender_1").style.display = "block";
}, 1000)
}
},
{
options: {
data: {
columns: [
["data1", 300],
["data2", 130]
],
type: "pie"
},
render: {
lazy: true,
observe: false
}
},
func: function(chart) {
setTimeout(function() { chart.flush(); }, 1000)
}
}
],
Padding: {
options: {
padding: {
Expand Down
3 changes: 3 additions & 0 deletions demo/simple-sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,6 @@ div.row {
#stanfordDiagram .test-polygon4 text { fill: black; }
#stanfordDiagram .test-polygon5 { fill: orange; }
#stanfordDiagram .test-polygon5 text { fill: black; }

/* Lazy Render */
#lazyRender_1 { display: none; }
52 changes: 52 additions & 0 deletions spec/internals/bb-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,56 @@ describe("Interface & initialization", () => {
});
});
});

describe("check for lazy rendering", () => {
const args = {
data: {
columns: [
["data1", 300, 350, 300]
]
}
};

before(() => {
// hide to lazy render
document.body.querySelector("#chart").style.display = "none";

chart = util.generate(args);
});

it("check lazy rendering & mutation observer", done => {
const el = document.body.querySelector("#chart");

expect(el.innerHTML).to.be.empty;

el.style.display = "block";

setTimeout(() => {
expect(el.innerHTML).to.be.not.empty;
done();
}, 500);
});

it("check lazy rendering via option", done => {
const el = document.body.querySelector("#chart");

args.render = {
lazy: true,
observe: false
};

chart = util.generate(args);

// chart shouldn't be rendered
expect(el.innerHTML).to.be.empty;

// call to render
chart.flush();

setTimeout(() => {
expect(el.innerHTML).to.be.not.empty;
done();
}, 500);
});
});
});
3 changes: 2 additions & 1 deletion spec/internals/data-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,12 @@ describe("DATA", () => {
setTimeout(() => {
const data = chart.data();

expect(chart.$.chart.selectAll("svg").size()).to.be.equal(1);
expect(data).to.not.be.null;
expect(data.length).to.be.equal(3);

done();
}, 300);
}, 500);
});

it("set options data.mimeType='json'", () => {
Expand Down
41 changes: 23 additions & 18 deletions src/api/api.chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,31 @@ extend(Chart.prototype, {
flush(soft, isFromResize) {
const $$ = this.internal;

// reset possible zoom scale
if (isFromResize) {
$$.brush && $$.brush.updateResize();

if ($$.rendered) {
// reset possible zoom scale
if (isFromResize) {
$$.brush && $$.brush.updateResize();
} else {
// re-update config info
$$.axis && $$.axis.setOrient();
}

$$.zoomScale = null;

soft ? $$.redraw({
withTransform: true,
withUpdateXDomain: true,
withUpdateOrgXDomain: true,
withLegend: true
}) : $$.updateAndRedraw({
withLegend: true,
withTransition: false,
withTransitionForTransform: false,
});
} else {
// re-update config info
$$.axis && $$.axis.setOrient();
$$.initToRender(true);
}

$$.zoomScale = null;

soft ? $$.redraw({
withTransform: true,
withUpdateXDomain: true,
withUpdateOrgXDomain: true,
withLegend: true
}) : $$.updateAndRedraw({
withLegend: true,
withTransition: false,
withTransitionForTransform: false,
});
},

/**
Expand Down
39 changes: 39 additions & 0 deletions src/config/Options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3380,6 +3380,45 @@ export default class Options {
radar_size_ratio: 0.87,
radar_direction_clockwise: false,

/**
* Control the render timing
* @name render
* @memberof Options
* @type {Object}
* @property {Boolean} [render.lazy=true] Make to not render at initialization (enabled by default when bind element's visibility is hidden).
* @property {Boolean} [render.observe=true] Observe bind element's visibility(`display` or `visiblity` inline css property value) & render when is visible automatically (for IEs, only works IE11+). When set to **false**, call [`.flush()`](./Chart.html#flush) to render.
* @see [Demo](https://naver.github.io/billboard.js/demo/#ChartOptions.LazyRender)
* @example
* render: {
* lazy: true,
* observe: true
* }
*
* @example
* <!-- render.lazy will detect visibility defined as inline or rule -->
* <div id='chart' style='display:none'></div>
*
* // render.lazy enabled by default when element is hidden
* var chart = bb.generate({ ... });
*
* // chart will be rendered automatically when element's visibility changes
* // Note: works only for inline attribute's value changes
* document.getElementById('chart').style.display = 'block';
*
* @example
* // chart won't be rendered and not observing bind element's visiblity changes
* var chart = bb.generate({
* render: {
* lazy: true,
* observe: false
* }
* });
*
* // call at any point when you want to render
* chart.flush();
*/
render: {},

/**
* Show rectangles inside the chart.<br><br>
* This option accepts array including object that has axis, start, end and class. The keys start, end and class are optional.
Expand Down
2 changes: 1 addition & 1 deletion src/data/data.convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extend(ChartInternal.prototype, {
throw Error("url or json or rows or columns is required.");
}

return data;
return isArray(data) && data;
},

convertUrlToData(url, mimeType = "csv", headers, keys, done) {
Expand Down
79 changes: 56 additions & 23 deletions src/internals/ChartInternal.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {transition as d3Transition} from "d3-transition";
import Axis from "../axis/Axis";
import CLASS from "../config/classes";
import {document, window} from "../internals/browser";
import {notEmpty, asHalfPixel, getOption, isValue, isArray, isFunction, isString, isNumber, isObject, callFn, sortValue} from "./util";
import {notEmpty, asHalfPixel, getOption, isValue, isArray, isDefined, isFunction, isString, isNumber, isObject, callFn, sortValue} from "./util";

/**
* Internal chart class.
Expand All @@ -35,6 +35,7 @@ export default class ChartInternal {
$$.data = {};
$$.cache = {};
$$.axes = {};
$$.rendered = false;
}

beforeInit() {
Expand All @@ -57,12 +58,62 @@ export default class ChartInternal {

init() {
const $$ = this;
const config = $$.config;

$$.initParams();

const convertedData = $$.convertData($$.config, $$.initWithData);
const bindto = {
element: config.bindto,
classname: "bb"
};

if (isObject(config.bindto)) {
bindto.element = config.bindto.element || "#chart";
bindto.classname = config.bindto.classname || bindto.classname;
}

// select bind element
$$.selectChart = isFunction(bindto.element.node) ?
config.bindto.element : d3Select(bindto.element || []);

convertedData && $$.initWithData(convertedData);
if ($$.selectChart.empty()) {
$$.selectChart = d3Select(document.body.appendChild(document.createElement("div")));
}

$$.selectChart.html("").classed(bindto.classname, true);
$$.initToRender();
}

/**
* Initialize the rendering process
* @param {Boolean} forced Force to render process
* @private
*/
initToRender(forced) {
const $$ = this;
const config = $$.config;
const target = $$.selectChart;
const isHidden = () => target.style("display") === "none" || target.style("visibility") === "hidden";

const isLazy = config.render.lazy || isHidden();
const hasObserver = isDefined(MutationObserver);

if (isLazy && hasObserver && config.render.observe !== false && !forced) {
new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.attributeName === "style" && !isHidden()) {
observer.disconnect();
!$$.rendered && $$.initToRender(true);
}
}
}).observe(target.node(), {attributes: true});
}

if (!isLazy || forced) {
const convertedData = $$.convertData(config, $$.initWithData);

convertedData && $$.initWithData(convertedData);
}
}

initParams() {
Expand Down Expand Up @@ -150,26 +201,6 @@ export default class ChartInternal {
$$.axis = new Axis($$);
config.zoom_enabled && $$.initZoom();

const bindto = {
element: config.bindto,
classname: "bb"
};

if (isObject(config.bindto)) {
bindto.element = config.bindto.element || "#chart";
bindto.classname = config.bindto.classname || bindto.classname;
}

// select bind element
$$.selectChart = isFunction(bindto.element.node) ?
config.bindto.element : d3Select(bindto.element || []);

if ($$.selectChart.empty()) {
$$.selectChart = d3Select(document.body.appendChild(document.createElement("div")));
}

$$.selectChart.html("").classed(bindto.classname, true);

// Init data as targets
$$.data.xs = {};
$$.data.targets = $$.convertDataToTargets(data);
Expand Down Expand Up @@ -336,6 +367,8 @@ export default class ChartInternal {

// export element of the chart
$$.api.element = $$.selectChart.node();

$$.rendered = true;
}

initChartElements() {
Expand Down
16 changes: 16 additions & 0 deletions types/options.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,22 @@ export interface ChartOptions {
* Set plugins
*/
plugins?: Stanford | any[];

/**
* Control the render timing
*/
redner?: {
/**
* Make to not render at initialization (enabled by default when bind element's visibility is hidden).
*/
lazy?: boolean;

/**
* Observe bind element's visibility(`display` or `visiblity` inline css property value) & render when is visible automatically (for IEs, only works IE11+).
* When set to **false**, call [`.flush()`](./Chart.html#flush) to render.
*/
observe?: boolean;
};
}

export interface AreaLinearGradientOptions {
Expand Down

0 comments on commit b6af77f

Please sign in to comment.