I have a list of charts. I use Chart.js to create those charts. Since my list can have 1 to 100 or more entries initializing all charts at once would not be smart because that would make the ui freeze for a long time. So instead I thought it would be much better to only initialize those charts which are visible inside the view bounds of the browser so that for example only the first chart is getting initialized and when the user scrolls down and the second canvas becomes visible the second is getting initialized and so on.
I have everything setup but the only problem that I have right now is: how can I create an eventlistener or anything similiar which I can add to each canvas element that gets triggered when a canvas becomes visible inside the view bounds of the browser so that i can perform the chart initialization for that canvas?
I'm the author of OnScreen, a small library that can call a callback function when a HTMLElement enters the viewport or the boundaries of its container.
// Uses ES6 syntax
import OnScreen from 'onscreen';
const os = new OnScreen();
os.on('enter', 'canvas', (element) => {
if (!element.chartInitialized) {
// Initialize the chart
// Update the `chartInitialized` property
// to avoid initializing it over and over
element.chartInitialized = true;
}
});
For more information, take a look at the documentation. Don't forget to check the demos repo for a couple simple examples.
I have used the onScreen jQuery plugin.
It is very easy. You just have to call for each canvas this:
$('elements').onScreen({
container: window,
direction: 'vertical',
doIn: function() {
// initialize canvas
},
doOut: function() {
// Do something to the matched elements as they get off scren
},
tolerance: 0,
throttle: 50,
toggleClass: 'onScreen',
lazyAttr: null,
lazyPlaceholder: 'someImage.jpg',
debug: false
});
Related
I'm building a responsive image viewer, incorporating OpenSeaDragon, that requires different panning behaviour at different screen widths. At narrow widths panning should not be allowed, but when the window's wider, panning should be activated.
A simplified example follows:
Let's assume the window is fairly wide, then the panning options during initialisation will be:
OpenSeaDragon({ panHorizontal: true, panVertical: true, ... });
(I understand these are the defaults, but including them here for clarity.)
I can then detect whether panning should be activated/deactivated using matchMedia inside a window resize event handler, something like:
// (Crude example, resize would need debouncing etc)
window.addEventListener('resize', function () {
if (window.matchMedia('(min-width:800px)').matches) {
// allow panning
} else {
// prevent panning
}
});
My question is, can the panning constraint options provided when OpenSeaDragin is initialised be changed later, without having to reinitialise the viewer? Failing that, is there a different way of getting the same effect? I've had a dig into the OpenSeaDragon docs and code but I can't see a way of doing it.
Yes, you can change those properties directly without having to reinitialize, like so:
var viewer = OpenSeaDragon({ panHorizontal: true, panVertical: true, ... });
viewer.panHorizontal = false;
I am using Scrollmagic in an angular js app. My app has a sidebar on right with infinite scrolling. There are sections which stick for a particular amount of time then unstick like below
Section A comes into view and sticks
Section A remains sticky for few pixels and then unsticks
Section B appears and sticks after few pixels
Section B remains sticky for few pixels and then unsticks and so on..
So in effect i create multiple scenes and add it to the scrollMagicController. I am now suspecting memory leaks due to scrollmagic retaining DOM nodes and heap profiler of devtools proves this. I know that there is a destroy method for scene and controller which needs to be called to clear things up but i can't figure out how to destroy multiple nodes. Below is what i have tried
var scrollMagicCtrl = new ScrollMagic.Controller();
//create scene dynamically whenever stickyContainerInView event fires
scope.$on('stickyContainerInView', function(event, inViewElement) {
new ScrollMagic.Scene({
triggerElement: someElement, //Selector or DOM object that defines the start of the scene
triggerHook: 'onLeave', //sets the position of trigger hook w.r.t viewport
duration: someDuration, //The duration(in pixels) for which the element will remain sticky
offset: -60 //Offset Value for the Trigger hook position
})
.setPin(someContainer)
.addTo(scrollMagicCtrl);
});
//clean up things on angular's destroy method
$scope.$on("$destroy", function() {
scrollMagicCtrl.destroy();
scrollMagicCtrl = null;
});
I guess i just need to call the controller's destroy method and it should automatically destroy all the scenes added to it but unfortunately it doesn't work that way.
Screenshot of heap profiler detached DOM node tree
Any help on how should i clear up things ?
I have a canvas created with KineticJs Stage. on this stage I have three layers. One is the background and is always on. The other two are overlays and the visibility is toggled by a checkbox. Any time the parent div of this stage resizes I redraw the entire stage to keep my layout correct. Here are the two situations where my toggling works:
1. before resize and redraw.
2. If I don't toggle it at all before the redraw.
Here is where it does not work:
1. Toggle layer on then off. Resize canvas to trigger redraw. Then try and toggle back on. In this case the visible attribute gets set to true when I call show() but the layer does not actually show up.
Stepping through the code I can not find any difference in the layer during any of the above scenarios. I did however notice that the index of each layer gets incremented each time it is rebuilt even though I have instantiated new instances of the stage and every layer each time I rebuild.
Can anybody tell me why the index increments even though everything has been destroyed and why the layer is not showing up? I was thinking zindex but this never seems to change and should be showing up.
here is what I do before every rebuild:
stage = new $window.Kinetic.Stage({
container: 'canvas',
x: 0,
y: 0,
width: parent.clientWidth,
height: parent.clientHeight
});
var layer = new $window.Kinetic.Layer();
erosionLayer = new $window.Kinetic.Layer({
visible: scope.erosionVisible
});
Here is where I toggle it:
scope.$watch('erosionVisible', function(val) {
if (!erosionLayer) return;
var showErosion = false;
if (!scope.erosionVisible) {
showErosion = false;
} else if (scope.erosionVisible) {
showErosion = true;
}
if (showErosion) {
erosionLayer.show();
} else {
erosionLayer.hide();
}
});
FYI, This is in an angularJs directive.
fixed the issue. for some reason I need to call draw on the layer when toggling if the stage has been rebuilt.
Given a HTML structure like so:
<div id="canvas">
<div class="group group-normal" id="1">hello world</div>
<div class="group group-infiltrated" id="2">whats up dogue</div>
</div>
I currently use jsPlumb to create a graph from that structure:
$(function() {
jsPlumb.Defaults.Container = $("div#canvas");
jsPlumb.draggable($("div.group"), {
containment: $("div#canvas")
});
var stateMachineConnector = {
connector: "Bezier",
paintStyle: { lineWidth: 2, strokeStyle: "#056" },
endpoint: "Blank",
anchor: "Continuous",
overlays:[ ["PlainArrow", { location: 1, width: 15, length: 12 } ]]
};
jsPlumb.connect({
source: "1",
target: "2"
}, stateMachineConnector);
});
Which already gives me a nice, draggable graph:
The problem is, that when initialising the graph, all the divs are in the upper left corner of the canvas. I learned that this is due that jsPlumb only provides functionality to layout a graph, but not to position it.
I then went on a scavenger hunt and found many useful libraries that deal with positioning, like springy, sigma and alike. The problem is: Most of the don't operate on divs, but rather on some (for me) instransparent SVG/Canvas object graph.
I like jsPlumb very much and would like to keep using it. The only thing I need from other libraries is the initial positioning of all the elements.
How can I make the elements be positioned more evenly over the space available, maybe even in a way that makes them arrange the same way every time the graph is initialized?
Here is the plugin for positioning DIV's which has connectivity: https://github.com/lndb/jsPlumb_Liviz.js
Basically the plugin is derived from graphviz js library.
You need to pass the connectivity information to the library through a textarea(id=dot-src). So whenever you establish a connection, store the connectivity info in a string and later pass it to the Liviz library for positioning it.
str='digraph MyGraph {'; // This string will be set to the textarea at the end.
// Whenever connection is created update the string.
jsPlumb.bind("jsPlumbConnection", function(ci) {
console.log($('#'+ci.sourceId).data("prop").dn+"->"+$('#'+ci.targetId).data("prop").dn);
str+=ci.sourceId+"->"+ci.targetId+";";
});
// At the end set the string and call Liviz function
$('#dot-src').text(str+"}"); // Set the string
w_launch(); // call the library to position the DIV's based on connectivity.
You can explore graphviz for different types of options to customise positioning.
I have some divs that have dynamic heights controlled by a 'click' function as below:
$('.expand').click(function() {
$(this).next('.collapse').slideToggle();
});
I am attempting to apply the jQuery wookmark plugin to the divs, and it works, apart from when their heights are dynamically resized by expanding one of the sections. From the documentation, I copied over one of the examples to my code, and the dynamic height works
$(document).ready(new function() {
// Prepare layout options.
var options = {
autoResize: true, // This will auto-update the layout when the browser window is resized.
container: $('#container'), // Optional, used for some extra CSS styling
offset: 30, // Optional, the distance between grid items
itemWidth: 300 // Optional, the width of a grid item
};
// Get a reference to your grid items.
var handler = $('.outerwrapper');
// Call the layout function.
handler.wookmark(options);
// Capture clicks on grid items.
handler.click(function(){
// Randomize the height of the clicked item.
var newHeight = $('img', this).height() + Math.round(Math.random()*300+30);
$(this).css('height', newHeight+'px');
// Update the layout.
handler.wookmark();
});
});
You can see this working here. How can I make it so that when you click one of the headings inside the divs, the layout updates, as it does in the example. Thanks in advance.
Usually third party jQuery plugins include some kind of "Refresh" or "Resize" function.
Taking a quick look at the function, it doesn't appear to have one; however, since there is an "autoResize" option (which will reload the layout on browser resize), you could simply create a click event that triggers the "resize" event like so:
JAVASCRIPT:
$("h1.resize").live("click", function()
{
$(window).trigger('resize');
});
http://api.jquery.com/trigger/
http://api.jquery.com/resize/
EDIT:
Re-reading the question again,
Looks like this:
handler.wookmark();
should refresh the layout (based on your posted code). So you should be able to use that instead of the resize trigger.
$("h1.resize").live("click", function()
{
handler.wookmark();
});