ZRender Source Code Analysis 2: Storage (Model layer), zrendermodel

Source: Internet
Author: User

ZRender Source Code Analysis 2: Storage (Model layer), zrendermodel
Review

Step 1: zrender Source Code Analysis 1: Overall Structure
This article analyzes M in the MVC structure of ZRender.

Overall understanding

As mentioned above, Storage is responsible for the Model in the MVC layer, that is, the model. For zrender, this Model is the shape object. x is not strong yet, to 2.x, in zr. when addShape () is used, the input parameter must be a new object. For more information, see here 2. x compared to 1. for more information about the change in x, that is, from 1. upgrade x to 2. in x, because the method has changed, you cannot get rid of all the code. It cannot be the same as ext. (upgrading from ExtJS3 to ExtJS4 is a particularly painful process ), therefore, in the original visualization program, we added the following helper (the program is based on ExtJS5)

Ext. define ('nt. utils. chartHelper ', {singleton: true, shapeMap :{}, requireMap :{},/*** get the shape constructor through the shape type * due to zrender upgrade, so this method appears. For details, * see: https://github.com/ecomfe/zrender/wiki/2.x%E7%9B%B8%E6%AF%941.x%E7%9A%84%E5%8F%98%E5%8C%96 ** @ Param shapeType shape type * @ returns {Constructor} */getShapeTypeConstructor: function (shapeType) {// The addShape of zrender2.0 cannot be used to add objects, only one initialized shape class can be added. // Therefore, each time require loads the required class, shapeMap is a cache object. // because echarts contains the source code of requirejs, however, the define and require methods are not exposed. // you have to modify the source code of echarts, window. require = require; if (! This. shapeMap [shapeType]) {this. shapeMap [shapeType] = require ('zrender/shape/'+ Ext. string. capitalize (shapeType);} return this. shapeMap [shapeType];},/*** create a shape Class Based on the shape type and input parameters, and the returned results can be directly added to addShape **. This method has multiple reloads, as follows ** 1. NT. utils. chartHelper. makeShapeInstance ('image', {scale: [1, 2], hover :....}); * 2. NT. utils. chartHelper. makeShapeInstance ({shape: 'image', scale: [1, 2], hover :....}); ** method in December 2nd Compatible with zrender1.x, the shape attribute can be shape | shapeType | type ** @ param shapeType shape type * @ param option parameter * @ returns {Object} shape Object */makeShapeInstance: function (shapeType, option) {if (Ext. isObject (shapeType) {option = shapeType; shapeType = option. shape | option. shapeType | option. type} var ctor = this. getShapeTypeConstructor (shapeType); if (! Ctor) new Error ('could not find this shape in zrender '); return new ctor (option );}});

In this way, we can continue playing as happily as before. Let's take a look at the overall structure of the Code.


Fortunately, the structure here is still super simple.

  • 1. This is a typical JS object creation structure. var Storage = function () {}; Storage. prototype. add = function (){.....};
  • 2. The method is appended to the protype, and the attribute is written in the constructor. this is returned for each method appended to the prototype. chained call is supported.
  • 3. Storage n. Storage; Storage location, warehouse; Storage device, power Storage (bottle); maintain all shapes, which can be viewed through some of the attributes.

Next, let's break through them one by one.

Constructor

Post the code first.

/*** Content warehouse (M) **/function Storage () {// map of all common shapes and id indexes this. _ elements = {}; // All shapes are arranged in the Z axis to improve the traversal performance. zElements [0] shape is under zElements [1] shape this. _ zElements = []; // The highlighted layer is in an unstable shape and dynamically added or deleted state. The array position is also in the Z axis direction, and the front is displayed below this. _ hoverElements = []; // maximum zlevel this. _ maxZlevel = 0; // zlevel with data changes this. _ changedZlevel = {};}

The author commented that this is a content warehouse, and think about it. This is not equivalent to a granary, and the shape object is a grain. In the constructor, _ elements, _ zElement, and _ hoverElements are the granary. The two variables _ elements and _ zElements actually store the same thing, but they are stored in a different way. Among them, z in the zElement variable is probably the meaning of zlevel. I think this is the core idea of zrender. Next we will use a (bei) (bi) method to look at the rendering in the memory. Open zrender. js and add a line of code: window. z = this;

Function ZRender (id, dom) {this. id = id; this. env = require ('. /tool/env '); this. storage = new Storage (); this. painter = new Painter (dom, this. storage); this. handler = new Handler (dom, this. storage, this. painter); window. z = this; // leak out z // animation control this. animatingShapes = []; this. animation = new Animation ({stage: {update: getAnimationUpdater (this)}); this. animation. start ();}

Then, run the following example:

require(['../src/zrender',    '../src/shape/Image',    '../src/shape/Text',    '../src/shape/Circle'],    function (zrender, ImageShape, TextShape, CircleShape) {    var box = document.getElementById('box');    var zr = zrender.init(box);    zr.addShape(new CircleShape({        style: {            x: 120,            y: 120,            r: 50,            color: 'red'        },        hoverable: true    }));    zr.addShape(new TextShape({        style: {            x: 220,            y: 220,            color: 'red',            text: 'something text'        },        hoverable: true,        zlevel: 2    }));    zr.render();});

Finally, enter z in the console and press enter to see the following print:


Obviously, the items in _ elements are directly inserted, regardless of the order. The items in zElements are stored according to the zlevel of the shape object, how to maintain it depends on how to add, delete, modify, and query

PS: This figure is very important. The process can be detailed during addition, deletion, modification, and query below.

Add
/*** Add ** @ param {Shape} shape parameter */Storage. prototype. add = function (shape) {shape. updateNeedTransform (); shape. style. _ rect = null; this. _ elements [shape. id] = shape; this. _ zElements [shape. zlevel] = this. _ zElements [shape. zlevel] | []; this. _ zElements [shape. zlevel]. push (shape); this. _ maxZlevel = Math. max (this. _ maxZlevel, shape. zlevel); this. _ changedZlevel [shape. zlevel] = true;/*** _ elements-> * {* _ zrender_101 _: shapeObject, * _ zrender_102 _: shapeObject, * _ zrender_103 _: shapeObject ,*... *} ** _ zrender_103 _ ** _ zElements-> * {* 1: [shapeObject, shapeObject], * 2: [shapeObject, shapeObject...], * 3. [...] **} ** 123 is the number of layers ** _ maxZlevel: 3 * changedZlevel: {1: true, 2: true ....} */return this ;};/*** add highlighted layer data ** @ param {Object} params parameter */Storage. prototype. addHover = function (params) {/*** a large push parameter is judged here to determine whether deformation is required. Transformers * Douban film: http://movie.douban.com/subject/7054604/ * When you first add the switch, you do not need to re-execute the */if (params. rotation & Math. abs (params. rotation [0])> 0.0001) | (params. position & (Math. abs (params. position [0])> 0.0001 | Math. abs (params. position [1])> 0.0001) | (params. scale & (Math. abs (params. scale [0]-1)> 0.0001 | Math. abs (params. scale [1]-1)> 0.0001) {params. needTransform = true;} else {params. needTransform = false;} this. _ hoverElements. push (params); // simply push the highlighted layer to _ hoverElements and return this ;};
  • 1. _ elements is stored with id as key and shape object as value
  • 2. _ zElements is an array that uses level as the array subscript. The shape object set of the same level forms an array as the value (if this layer is not initialized, there will be an initialization process)
  • 3. Each add operation resets the _ maxZlevel variable, which always indicates the maximum level; _ changedZlevel is an object that indicates the changed level (if changed, it will be repainted in painter)
  • 4. When addHover is used, the needTransform parameter is preprocessed first, and then the shape object is directly inserted into the _ hoverElements array without complicated processing.
Delete
/*** Delete highlighted layer data */Storage. prototype. delHover = function () {this. _ hoverElements = []; return this ;};/*** Delete. If shapeId is not specified, all objects are cleared ** @ param {string = | Array} idx Unique Identifier */Storage. prototype. del = function (shapeId) {if (typeof shapeId! = 'Undefined') {var delMap = {};/*** handle various reloads * 1. if it is not an array, add it directly to delMap * 2. if it is an array, traverse it */if (! (ShapeId instanceof Array) {// a single delMap [shapeId] = true;} else {// batch Delete if (shapeId. lenth <1) {// empty array return;} for (var I = 0, l = shapeId. length; I <l; I ++) {delMap [shapeId [I]. id] = true ;}}var newList; var oldList; var zlevel; var zChanged ={}; for (var sId in delMap) {if (this. _ elements [sId]) {zlevel = this. _ elements [sId]. zlevel; this. _ changedZlevel [zlevel] = true;/*** zEl is mainly used here. Deletion of elements in ements * Here we confirm that each zlevel is traversed only once, because once this if is entered, the flag is set to false at the end of the if, next time, you cannot enter ** 1. traverse delMap, retrieve the zlevel of a single shape, and then retrieve all from _ zElements [zlevel], named oldList * 2. traverse oldList. If no shape is currently traversed in delMap, add it to newList. The _ zElements [zlevel] of this layer is newList * 3. set the flag to false, indicating that the layer has been processed, so do not process it again */if (! ZChanged [zlevel]) {oldList = this. _ zElements [zlevel]; newList = []; for (var I = 0, l = oldList. length; I <l; I ++) {if (! DelMap [oldList [I]. id]) {newList. push (oldList [I]) ;}} this. _ zElements [zlevel] = newList; zChanged [zlevel] = true;} // delete a shape from _ elements to delete this. _ elements [sId] ;}} else {// do not specify shapeId to clear this. _ elements = {}; this. _ zElements = []; this. _ hoverElements = []; this. _ maxZlevel = 0; // maximum zlevel this. _ changedZlevel ={// zlevel with data changes all: true };} return this ;};
  • 1. The delHover method is very simple. Clear the content in _ hoverElements and return this
  • 2. For the del method, if shapeId is not input, all shapes will be deleted, all warehouse variables will be cleared, and all: true indicates that all layers will be repainted.
  • 3. process the parameter overloading. If it is an array, traverse it.
  • 4. Is there a problem with shapeId instanceof in some cases? Why not use Object. prototype. toString. call (xxx) = '[ object Array] 'For readability?
  • 5. delete this. _ elements [sId] For _ elements, but it takes some time to delete _ zElements. For more information, see the comments in the code.
Change
/*** Modify ** the unique ID of @ param {string} idx * @ param {Object} params parameter */Storage. prototype. mod = function (shapeId, params) {var shape = this. _ elements [shapeId]; if (shape) {shape. updateNeedTransform (); shape. style. _ rect = null; this. _ changedZlevel [shape. zlevel] = true; // there may be no more than one layer before and after modification/*** merge parameters, params & util. merge (shape, params, true); ** this. _ changedZlevel [shape. zlevel] = true; this is to prevent: ** var ImageShape = new ImageShape ({src: 'xxx.png ', zlevel: 1}); * imageShape. mod ({zlevel: 3}); ** here: level1 and level3 have both changed, and _ maxZlevel has also changed. */If (params) {util. merge (shape, params, true);} this. _ changedZlevel [shape. zlevel] = true; // It is possible that this is not a layer before and after modification. _ maxZlevel = Math. max (this. _ maxZlevel, shape. zlevel);} return this ;};
  • 1. The updateNeedTransform method is also a problem of preprocessing transformers.
  • 2. To prevent the problem that the shape object is not on the same layer when being modified, this. _ changedZlevel [shape. zlevel] = true is executed before and after it is executed. Although it is copeat, it is also necessary.
  • 3. The role of util. merge is to merge the newly added params into the original parameters. The specific code is no longer arrogant.
  • 4. Finally, reset _ maxZlevel to ensure the index when traversing the Z axis.
Query
/*** Traversal iterator ** @ param {Function} fun iterative callback Function, return true terminate iteration * @ param {Object =} option iteration parameter, by default, only the general shape is traversed in descending order * hover: true: whether to iterate the highlighted layer data * normal: 'down' | 'up' | 'free' whether to iterate the conventional data, whether to specify the zaxis sequence during iteration */Storage. prototype. iterShape = function (fun, option) {/*** handle default options = option | {hover: false, normal: 'lowdown '}; */if (! Option) {option = {hover: false, // do not traverse the highlighted layer normal: 'low' // high level priority};} if (option. hover) {// data traversal at the highlighted layer for (var I = 0, l = this. _ hoverElements. length; I <l; I ++) {if (fun (this. _ hoverElements [I]) {return this ;}} var zlist; var len; if (typeof option. normal! = 'Undefined') {// Z axis traversal: 'low' | 'up' | 'free' switch (option. normal) {case 'drop': // descending traversal, top priority var l = this. _ zElements. length; while (l --) {zlist = this. _ zElements [l]; if (zlist) {len = zlist. length; while (len --) {if (fun (zlist [len]) {return this ;}}} break; case 'up': // ascending traversal, underlying priority for (var I = 0, l = this. _ zElements. length; I <l; I ++) {zlist = this. _ zElements [I]; if (zlist) {len = zlist. length; for (var k = 0; k <len; k ++) {if (fun (zlist [k]) {return this ;}}} break; // case 'free': default: // unordered traversal for (var I in this. _ elements) {if (fun (this. _ elements [I]) {return this ;}} break ;}} return this ;}; /*** obtain the corresponding shape Attribute Based on the specified shapeId ** @ param {string =} unique ID of idx */Storage. prototype. get = function (shapeId) {return this. _ elements [shapeId] ;}; Storage. prototype. getMaxZlevel = function () {return this. _ maxZlevel;}; Storage. prototype. getChangedZlevel = function () {return this. _ changedZlevel;}; Storage. prototype. clearChangedZlevel = function () {this. _ changedZlevel ={}; return this ;}; Storage. prototype. setChangedZlevle = function (level) {this. _ changedZlevel [level] = true; return this ;}; Storage. prototype. hasHoverShape = function () {return this. _ hoverElements. length> 0 ;};
  • 1. iterShape can be divided into three traversal methods (unordered free, top-down, bottom-up). There is a switch (whether to traverse the hover of the highlighted layer)
  • 2. If no option is specified, set the default value. Do not traverse the highlighted layer and traverse from top to bottom.
  • 3. if you need to traverse the highlighted layer, traverse the _ hoverElements array, and call the callback function fun, if the return value of fun can be converted to true, return directly, I wonder if I can return it when it is false like jQuery's each, so I don't have to return false at the end of the function every time ?)
  • 4. if the _ zElemements array is traversed during down and up, because the number of layers may be separated, it will determine whether it is undefined each time it is retrieved. If there is a value, traverse the array and execute the fun callback. The return logic is the same as the previous one.
  • 5. If it is unordered traversal, it is best to traverse the _ elements array to call fun
  • 6. for get (get shape object by id)/getMaxZlevel (get maximum level)/getChangedZlevel (get changed level object)/clearChangedZlevel (Clear level change) /setChangedZlevle (set a level change to true)/hasHoverShape (whether a highlighted layer exists) are relatively simple and will not be detailed.
Summary
  • 1. In fact, this Storage is easy to understand. It mainly involves adding, deleting, modifying, and querying the Shape object. (I won't talk about the advantages of encapsulation. Let's just make up your mind)
  • 2. It can be seen that the author understands these new users very well and the code is quite understandable. I like it (hate jQuery). I guess, don't spray me.
  • 3. Another drift method is not mentioned. Let's talk about it later.

Struts2 requires model layer

Required. struts will automatically encapsulate your data. after entering the control layer, you can access the data as an object.

Get the parameter value. transcoding is encapsulated in the filter.

However, the premise is that the format of the object. Attribute should be entered on the page. You can have a good understanding of the strtuts2 principle and source code!


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.