
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>LibCanvas :: Solar</title> <link href="/files/styles.css" rel="stylesheet" /> <style> html, body { background: url(im/sky.png) } </style> <script src="/files/js/atom.js"></script> <script src="/files/js/libcanvas.js"></script> </head> <body> <p><a href="/">Return to index</a></p> <script> new function () { LibCanvas.extract(); atom.dom(function () { new Solar.Controller(); }); }; </script> <script src="js/controller.js"></script> </body> </html> /** @class Solar.Controller */ atom.declare('Solar.Controller', { initialize: function () { this.size = new Size(840, 840); this.app = new App({ size: this.size }); atom.ImagePreloader.run({ planets: 'im/planets.png', sun : 'im/sun.png' }, this.start.bind(this)); }, start: function (images) { // images ready this.app.resources.set( 'images', images ); } }); intersection: all so that they all redrawn and invoke: true so that each of them onUpdate method /** @class Solar.Controller */ // .. initialize: function () { // .. this.geoLayer = this.app.createLayer({ name: 'geo', invoke: true, intersection: 'all', zIndex: 2 }); // .. App.Element so that you can add to the application: /** @class Solar.Sun */ atom.declare('Solar.Sun', App.Element, { renderTo: function (ctx, resources) { ctx.drawImage({ image : resources.get('images').get('sun'), center: this.shape.center }); } }); /** @class Solar.Controller */ // .. start: function (images) { // .. this.sun = new Solar.Sun(this.geoLayer, { shape: new Circle(this.app.rectangle.center, 50) }); // .. 
/** @class Solar.Controller */ atom.declare('Solar.Controller', { names : 'Selene Mimas Ares Enceladus Tethys Dione Zeus Rhea Titan Janus Hyperion Iapetus' .split(' '), // .. start: function (images) { // .. for (var i = 12; i--;) { var planet = new Solar.Planet(this.geoLayer, { sun : this.sun, radius: 90 + i * 26, time : 40 + i * 20, image : i, zIndex: 0, name : this.names[i] }); } // .. this.center = this.solarCenter.clone(); this.center.move([ this.radius, 0 ]); 'time' in seconds or 360/1000 in time in milliseconds: this.rotatePerMs = (360).degree() / 1000 / this.settings.get('time'); getImagePart: function () { var x = this.settings.get('image'); return this.layer.app.resources.get('images') .get('planets') .sprite(new Rectangle([ x*this.size.width,0 ],this.size)); }, LibCanvas.Point.rotate method. normalizeAngle needed so that the angle is always between 0 and 360 degrees. rotate: function (angle) { if (angle == null) angle = Number.random(0, 360).degree(); this.angle = (this.angle + angle).normalizeAngle(); this.center.rotate(angle, this.solarCenter); return this; }, onUpdate add interactivity - every call to the onUpdate method onUpdate planet, not forgetting to make an amendment for the time that has passed since the previous call. onUpdate , like onUpdate , are renderTo built into the LibCanvas framework that can be overridden and changed behavior. onUpdate: function (time) { this.rotate(time * this.rotatePerMs); this.redraw(); }, renderTo: function (ctx) { ctx.drawImage({ image : this.image, center: this.center, angle : this.angle }); } /** @class Solar.Planet */ atom.declare('Solar.Planet', App.Element, { angle: 0, configure: function () { this.size = new Size(26, 26); this.center = this.solarCenter.clone(); this.center.move([ this.radius, 0 ]); this.rotatePerMs = (360).degree() / 1000 / this.settings.get('time'); this.shape = new Circle(this.center, this.size.width/2); this.image = this.getImagePart(); this.rotate(); }, getImagePart: function () { var x = this.settings.get('image'); return this.layer.app.resources.get('images') .get('planets') .sprite(new Rectangle([x*this.size.width,0],this.size)); }, get radius () { return this.settings.get('radius'); }, get solarCenter () { return this.settings.get('sun').shape.center; }, rotate: function (angle) { if (angle == null) angle = Number.random(0, 360).degree(); this.angle = (this.angle + angle).normalizeAngle(); this.center.rotate(angle, this.solarCenter); return this; }, onUpdate: function (time) { this.rotate(time * this.rotatePerMs); this.redraw(); }, renderTo: function (ctx) { ctx.drawImage({ image : this.image, center: this.center, angle : this.angle }); } }); 
onUpdate call here, and we will manage the intersections manually. All the same, these intersections of objects with each other, as we see, especially and no. In addition, in order not to return, immediately add a method to create an orbit on the planet /** @class Solar.Controller */ // .. initialize: function () { // .. this.orbitLayer = this.app.createLayer({ name: 'orbit', intersection: 'manual', zIndex: 1 }); // .. start: function (images) { // .. for (var i = 12; i--;) { var planet = new Solar.Planet(this.geoLayer, { // .. }); planet.createOrbit(this.orbitLayer, i); // <=== // .. } // .. /** @class Solar.Planet */ // .. createOrbit: function (layer, z) { return this.orbit = new Solar.Orbit(layer, { planet: this, zIndex: z }); }, // .. Circle , which will be the basis of our drawing, in the renderTo method renderTo simply stroke this circle. The only thing that has now been added is the clearPrevious method, which changes the principle of cleaning the layer from this object - we do not roughly clear the contents of the BoundingRectangle, but carefully draw the circle stroke using the inverted ctx.clear(this.shape, true) : /** @class Solar.Orbit */ atom.declare('Solar.Orbit', App.Element, { configure: function () { this.shape = new Circle(this.planet.solarCenter, this.planet.radius); }, get planet () { return this.settings.get('planet'); }, clearPrevious: function (ctx) { ctx.clear(this.shape, true); }, renderTo: function (ctx, resources) { ctx.stroke(this.shape, 'rgba(0,192,255,0.5)'); } }); 
LibCanvas.Mouse object that catches mouse events of a specific dom element and a LibCanvas.App.MouseHandler object that will process these events and redirect to the corresponding application element. /** @class Solar.Controller */ // .. start: function (images) { var mouse, mouseHandler; mouse = new Mouse(this.app.container.bounds); mouseHandler = new App.MouseHandler({ mouse: mouse, app: this.app }); this.app.resources.set({ images: images, mouse : mouse, mouseHandler: mouseHandler }); // .. for (var i = 12; i--;) { var planet = new Solar.Planet(this.geoLayer, { // .. }); planet.createOrbit(this.orbitLayer, i); mouseHandler.subscribe( planet ); mouseHandler.subscribe( planet.orbit ); } } // .. isTriggerPoint method. Now mouse events will fire into orbit only within 13 pixels from it. /** @class Solar.Orbit */ // .. isTriggerPoint: function (point) { var distance = this.planet.solarCenter.distanceTo(point); return (this.planet.radius - distance).abs() < 13; }, // .. Clickable behavior and slightly modify the rendering method: /** @class Solar.Orbit */ // .. configure: function () { // .. new App.Clickable( this, this.redraw ).start(); }, // .. renderTo: function (ctx, resources) { if (this.hover) { ctx.stroke(this.shape, 'rgba(255,64,64,0.8)'); } else { ctx.stroke(this.shape, 'rgba(0,192,255,0.5)'); } } // .. 
/** @class Solar.Orbit */ // .. isHover: function () { return this.hover || this.planet.hover; }, renderTo: function (ctx, resources) { if (this.isHover()) { // .. } // .. redraw to onUpdate /** @class Solar.Planet */ // .. configure: function () { // .. new App.Clickable( this, this.redraw ).start(); }, // .. onUpdate: function (time) { // .. if (this.orbit.isHover()) this.orbit.redraw(); }, // .. /** @class Solar.Orbit */ // .. renderTo: function (ctx, resources) { if (this.isHover()) { ctx.save(); ctx.set({ strokeStyle: 'rgb(0,192,255)', lineWidth: 3 }); ctx.stroke(this.shape); ctx.clear(this.planet.shape); ctx.stroke(this.planet.shape); ctx.restore(); } else { ctx.stroke(this.shape, 'rgba(0,192,255,0.5)'); } } saveCurrentBoundingShape , which will save exactly where the hernia was last, if any, at all. It will also be called when necessary automatically: /** @class Solar.Orbit */ // .. saveCurrentBoundingShape: function () { if (this.isHover()) { this.previousBoundingShape = this.planet.shape.clone().grow(6); } else { this.previousBoundingShape = null; } return this; }, /** @class Solar.Orbit */ // .. clearPrevious: function (ctx) { if (this.previousBoundingShape) { ctx.save(); ctx.set({ lineWidth: 4 }); ctx.clear(this.previousBoundingShape); ctx.clear(this.shape, true); ctx.restore(); } else { ctx.clear(this.shape, true); } }, 
mouseout on Solar.Planet , but they only work when the mouse is moving, i.e. if the planet leaves the stationary mouse, the mouseout will not work. Therefore, we take the point of the mouse and every frame we check if it is located above our planet. /** @class Solar.Planet */ // .. configure: function () { // .. this.mousePoint = this.layer.app.resources.get('mouse').point; this.info = new Solar.Info(this.layer, { planet: this, zIndex: 1 }); }, checkStatus: function (visible) { if (this.info.isVisible() != visible) { this.info[visible ? 'show' : 'hide'](); } }, onUpdate: function (time) { // .. this.checkStatus(this.isTriggerPoint(this.mousePoint)); // if (this.info.isVisible()) this.info.updateShape(this.shape.center); }, // .. settings: { hidden: true } is one of the LibCanvas settings. This element will not participate in drawing in any way, but it still catches mouse events if it is signed. Therefore, we create a simple and logical class Solar.Info using this feature. /** @class Solar.Info */ atom.declare('Solar.Info', App.Element, { settings: { hidden: true }, get planet () { return this.settings.get('planet'); }, configure: function () { this.shape = new Rectangle(0,0,100,30); }, updateShape: function (from) { this.shape.moveTo(from).move([20,10]) }, show: function () { this.settings.set({ hidden: false }); this.redraw(); }, hide: function () { this.settings.set({ hidden: true }); this.redraw(); }, renderTo: function (ctx) { ctx.fill(this.shape, '#002244'); ctx.text({ to : this.shape, text : this.planet.settings.get('name'), color: '#0ff', align: 'center', optimize: false, padding: 3 }) } }); 
/** @class Solar.Planet */ // .. checkStatus: function (visible) { if (this.info.isVisible() != visible) { this.info[visible ? 'show' : 'hide'](); this.layer.dom.element.css('cursor', visible ? 'pointer' : 'default'); } }, // .. (program) - the better. On an empty application, it reaches 100%.




Source: https://habr.com/ru/post/163893/
All Articles