How to clip a view in Famo.us? - famo.us

I'm trying to build a countdown with Famous Timer.
As a first step, I want to make a scrolling digit, so I made 3 digits which are doing the needed animation and now I need to show only the middle one.
I saw the clipSize option, but couldn't understand how to use it.
If there are some other way to do it, that's great too.
My app is here: http://slexy.org/view/s2R8VNhgEO
Thanks, Alex A.

Rather than fix your code, I wrote an example of how you can create the effect you are looking for with the Lightbox render controller and clipping the view to only show the current count down index. Of course, this can be improved as needed.
Example of the working code in jsBin: Just click to start the counter.
Main Context
var mainContext = Engine.createContext();
var cview = new CountdownView({
start: 10,
size: [50, 50]
});
var counter = 0;
var started = false;
var funcRef;
cview.on('click', function () {
if (started) {
Timer.clear(funcRef);
started = false;
} else {
started = true;
funcRef = Timer.setInterval(function(){
console.log('setNext ' + cview.nextIndex);
cview.setNext();
},1000);
}
});
var container = new ContainerSurface({
size: [100, 100],
properties: {
overflow: 'hidden'
}
});
container.add(cview);
mainContext.add(container);
CountdownView
function CountdownView(options) {
View.apply(this, arguments);
this.options.start = options.start || 10;
this.surfaces = [];
for (var i = 0; i <= this.options.start; i++) {
var surface = new Surface({
size: this.options.size,
content: i.toString(),
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: this.options.size[1]+"px",
textAlign: "center",
fontSize: "30px",
cursor:'pointer'
}
});
this.surfaces.push(surface);
surface.pipe(this._eventOutput);
}
this.renderer = new Lightbox({
inOpacity: 0,
outOpacity: 0,
inOrigin: [1, 1],
inAlign: [1, 1],
showOrigin: [0, 0],
inTransform: Transform.translate(0,0,0.0002),
outTransform: Transform.translate(0,this.options.size[1],0.0001),
outOrigin: [1,1],
outAlign: [1,1],
inTransition: { duration: 600, curve: Easing.inCirc },
outTransition: { duration: 1000, curve: Easing.outCirc },
overlap: true
});
this.add(this.renderer);
this.renderer.show(this.surfaces[this.options.start]);
this.nextIndex = this.options.start - 1;
}
CountdownView.prototype = Object.create(View.prototype);
CountdownView.prototype.constructor = CountdownView;
CountdownView.prototype.setNext = function setNext() {
this.renderer.show(this.surfaces[this.nextIndex]);
this.nextIndex = (this.nextIndex -1 < 0) ? this.options.start : this.nextIndex - 1;
};
CountdownView.prototype.setIndex = function setIndex(newIndex) {
if (newIndex < 0 || newIndex > this.countStart) return;
this.renderer.show(this.surfaces[newIndex]);
};
CountdownView.prototype.getLength = function getLength() {
return this.surfaces.length;
};

Related

Custom plugin to show tooltips, on hover

I have made a custom plugin to always show the tooltips since that was what I needed.
The only thing I would like to have now is a on-hover action.
const alwaysShowTooltipPlugin = {
id: 'alwaysShowTooltip',
onHover: function (evt, item, legend) {
console.log('hovered')
},
afterDraw(chart, args, options) {
const { ctx } = chart
ctx.save()
chart.data.datasets.forEach((dataset, i) => {
chart.getDatasetMeta(i).data.forEach((datapoint, index) => {
const { x, y } = datapoint.tooltipPosition();
const text = dataset.label
const textWidth = ctx.measureText(text).width
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'
ctx.fillRect(x - ((textWidth + 20) / 2), y - 40, textWidth + 20, 30)
// Triangle
ctx.beginPath()
ctx.moveTo(x, y )
ctx.lineTo(x - 5, y - 10)
ctx.lineTo(x + 5, y - 10)
ctx.fill()
ctx.restore()
// Text
ctx.font = '14px Arial'
ctx.fillStyle = 'white'
ctx.fillText(text, x - (textWidth / 2), y - 19)
ctx.restore()
})
})
},
}
How can I add a eventListener for a onClick so that I can use the data that was clicked on? The onHover I have currently does not work. It doesn't do anything at all.
I am using Nuxt by the way and Vue-ChartJS latest version.

Chart.js getElementsAtEvent function not recognized for onclick on bar chart

I am new to Chart.Js and facing issues on click event on bar chart. I would like to get the repective x-axis lable on click function . But I am receiving error "barchart.getElementsAtEvent is not a function at HTMLCanvasElement.Graphinit.document.getElementById.onclick". I really do not understand what exactly is wrong in my code.
I am calling the below function to generate bar chart The min, item are data array for x-axis & yaxis.
cdn : "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"
var barchart;
function Graphinit(min, item, labelstring, stepcount, scaleStepWidth, reporttype, min1, item1) {
$('#myChart').remove(); // this is my <canvas> element
$('#showgraph').append(' <canvas id="myChart" width="1200" height="400"></canvas>');
inputdata = min;
xaxislabel = item;
Chart.types.Bar.extend({
name: "BarAlt",
draw: function () {
Chart.types.Bar.prototype.draw.apply(this, arguments);
var ctx = this.chart.ctx;
ctx.save();
// text alignment and color
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ctx.fillStyle = this.options.scaleFontColor;
// position
var x = this.scale.xScalePaddingLeft * 0.4;
var y = this.chart.height / 2;
// change origin
ctx.translate(x, y)
// rotate text
ctx.rotate(-90 * Math.PI / 180);
ctx.fillText(this.datasets[0].label, 0, 0);
ctx.restore();
}
});
var bridgeDataPoints = [];
if ((reporttype === "Lines picked per user") || (reporttype === "Picks per item") || (reporttype === "Lines Picked by item class") || (reporttype == "Lines picked by customer") || (reporttype == "Total Lines Picked") || reporttype == "Skipped Lines") {
for (var j = 0; j < min.length; j++) {
var dataPoint = new DataPoint1(item[j], min[j]);
bridgeDataPoints.push(dataPoint);
}
}
var data = {
labels: bridgeDataPoints,
datasets: [{
label: labelstring,
fillColor: "rgba(255, 255, 255, 1)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: inputdata
}]
};
var barchartoptions = {
//Boolean - If we show the scale above the chart data
scaleOverlay: false,
//Boolean - If we want to override with a hard coded scale
scaleOverride: true,
//** Required if scaleOverride is true **
//Number - The number of steps in a hard coded scale
scaleSteps: stepcount,
//Number - The value jump in the hard coded scale
scaleStepWidth: scaleStepWidth,
//Number - The scale starting value
scaleStartValue: 0,
scaleEndValue: stepcount,
//String - Colour of the scale line
scaleLineColor: "rgba(0,0,0,.1)",
//Number - Pixel width of the scale line
scaleLineWidth: 1,
//Boolean - Whether to show labels on the scale
scaleShowLabels: true,
//Interpolated JS string - can access value
scaleLabel: " <%=value%>",
//String - Scale label font declaration for the scale label
scaleFontFamily: "'Arial'",
//Number - Scale label font size in pixels
scaleFontSize: 12,
//String - Scale label font weight style
scaleFontStyle: "normal",
//String - Scale label font colour
scaleFontColor: "#666",
///Boolean - Whether grid lines are shown across the chart
scaleShowGridLines: true,
//String - Colour of the grid lines
scaleGridLineColor: "rgba(0,0,0,.05)",
//Number - Width of the grid lines
scaleGridLineWidth: 1,
//Boolean - If there is a stroke on each bar
barShowStroke: true,
//Number - Pixel width of the bar stroke
barStrokeWidth: 2,
//Number - Spacing between each of the X value sets
barValueSpacing: 10,
//Number - Spacing between data sets within X values
barDatasetSpacing: 3,
//Boolean - Whether to animate the chart
animation: true,
//Number - Number of animation steps
animationSteps: 60,
//String - Animation easing effect
animationEasing: "easeOutQuart",
//Function - Fires when the animation is complete
onAnimationComplete: function (animation) {
//alert('onAnimationComplete')
},
tooltipTemplate: "<%if (label){%><%=label.tooltip%><%}%>"
};
barchartoptions.datasetFill = false;
var ctx = $("#myChart").get(0).getContext('2d');
ctx.canvas.height = 400; // setting height of canvas
ctx.canvas.width = 1200; // setting width of canvas
//var config = Chart.defaults.bar;
barchart = new Chart(ctx).BarAlt(data, barchartoptions);
var length = inputdata.length; //min1.length;
var backgroundcolorset = [];
for (var i = 0; i < length; i++) {
var rgb = [];
for (var j = 0; j < 3; j++) {
rgb.push(Math.floor(Math.random() * 255));
}
backgroundcolorset.push('rgb(' + rgb.join(',') + ')');
}
for (var j = 0; j < length; j++) {
barchart.datasets[0].bars[j].fillColor = backgroundcolorset[j]; //bar 1
}
barchart.update();
console.log()
//console.log(barchart.getElementAtEvent(e));
document.getElementById("myChart").onclick = function (evt) {
// var activePoints =
console.log(barchart);
var activePoints = barchart.getElementsAtEvent(evt);
};
} // end Graphinit

Famo.us: why won't the width of the clicked surface change?

Famous.Engine = famous.core.Engine;
Famous.Surface = famous.core.Surface;
Famous.RenderNode = famous.core.RenderNode;
Famous.ContainerSurface = famous.surfaces.ContainerSurface;
Famous.ScrollView = famous.views.Scrollview;
Famous.SequentialLayout = famous.views.SequentialLayout;
Famous.Transform = famous.core.Transform;
Famous.Transitionable = famous.transitions.Transitionable;
Famous.SnapTransition = famous.transitions.SnapTransition;
Famous.TransitionableTransform = famous.transitions.TransitionableTransform;
Famous.Modifier = famous.core.Modifier;
Famous.StateModifier = famous.modifiers.StateModifier;
Famous.Easing = famous.transitions.Easing;
Famous.EventHandler = famous.core.EventHandler;
var projectsList = document.getElementById('projects-list');
var mainContext = Famous.Engine.createContext(projectsList);
var sequentialLayout = new Famous.SequentialLayout({
direction: 0
});
Famous.Transitionable.registerMethod('snap', Famous.SnapTransition);
var snap = { method: 'snap', period: 600, dampingRatio: 0.6 }
var surfaces = [];
for (var i = 0; i < 20; i++) {
var surface = new Famous.Surface({
content: "Surface: " + (i + 1),
size: [300, $(window).height()],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
textAlign: "center"
}
});
surface.open = false;
surface.state = new Famous.Modifier({
size: [undefined, undefined]
});
surface.trans = new Famous.Transitionable(300);
surface.state.sizeFrom(function(){
return [this.trans.get(), undefined];
}.bind(surface));
surface.node = new Famous.RenderNode();
surface.node.add(surface.state).add(surface);
surface.pipe(sequentialLayout);
surface.on('click',function(e){
if (this.open) {
this.trans.halt();
this.trans.set(300, snap);
} else {
this.trans.halt();
this.trans.set($(window).width(), snap);
}
this.open = !this.open;
}.bind(surface));
surfaces.push(surface.node);
sequentialLayout.sequenceFrom(surfaces);
}
mainContext.add(sequentialLayout);
The surfaces translate their X positions fine, but do not change their width from 300px. I would also like the surface that's clicked on to change its width to fill the window's width if its in a closed state. I would like the surface to go back to its initial width of 300px if it is clicked in an open state. How do I accomplish this so the width animates rather than jumps to the specified sizes?
Remove the size of the Surface
var surface = new Famous.Surface({
content: "Surface: " + (i + 1),
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
textAlign: "center"
}
});
The key is to not specify a static width or height when creating the surfaces:
var surface = new Famous.Surface({
content: "Surface: " + (i + 1),
size: [undefined, undefined], /* removed the original [300, $(window).height()] */
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
textAlign: "center"
}
});

jsPlumb connecting raphael element's

I used raphael.js to draw some element's, and now I want to connect them,
how to connect raphael elements with jsplumb ?
For example, in the JSFiddle below : I'm trying to create connections between 2 circles, but I can't, because the circles have no id, my code .
i can't use this :
jsPlumb.connect({source:"id1", target:"id2"})
I finally found a solution to connect raphael elements with jsplumb, I want to thank Mr: # Simon Porritt (jsPlumb creator) for his help: the solution
jsPlumb.ready(function(){
jsPlumb.Defaults.Container = "drawing_board";
// Create a 480 x 640 canvas.
var paper = Raphael('drawing_board');
// of 90 pixels.
var circle1 = paper.circle(140, 110, 90).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var circle2 = paper.circle(400, 180, 90).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var rect = paper.rect(50, 280, 90, 70).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var ellipse = paper.ellipse(300, 420, 90, 70).attr({ fill: '#3D6AA2', stroke: '#000000', 'stroke-width': 8 });
var offsetCalculators = {
"CIRCLE":function(el, parentOffset) {
var cx = parseInt(el.attr("cx"), 10),
cy = parseInt(el.attr("cy"), 10),
r = parseInt(el.attr("r"), 10);
return {
left: parentOffset.left + (cx - r),
top:parentOffset.top + (cy - r)
};
},
"ELLIPSE":function(el, parentOffset) {
var cx = parseInt(el.attr("cx"), 10),
cy = parseInt(el.attr("cy"), 10),
rx = parseInt(el.attr("rx"), 10),
ry = parseInt(el.attr("ry"), 10);
return {
left: parentOffset.left + (cx - rx),
top:parentOffset.top + (cy - ry)
};
},
"RECT":function(el, parentOffset) {
var x = parseInt(el.attr("x"), 10),
y = parseInt(el.attr("y"), 10);
return {
left: parentOffset.left + x,
top:parentOffset.top + y
};
}
};
var sizeCalculators = {
"CIRCLE":function(el) {
var r = parseInt(el.attr("r"), 10);
return [ r * 2, r * 2 ];
},
"ELLIPSE":function(el) {
var rx = parseInt(el.attr("rx"), 10),
ry = parseInt(el.attr("ry"), 10);
return [ rx * 2, ry * 2 ];
},
"RECT":function(el) {
var w = parseInt(el.attr("width"), 10),
h = parseInt(el.attr("height"), 10);
return [ w, h ];
}
};
jsPlumb.CurrentLibrary.getOffset = function(el) {
el = $(el);
var del = el[0], tn = del.tagName.toUpperCase();
if (offsetCalculators[tn]) {
var so = el.parent().offset();
return offsetCalculators[tn](el, so);
}
else
return el.offset();
};
jsPlumb.CurrentLibrary.getSize = function(el) {
el = $(el);
var del = el[0], tn = del.tagName.toUpperCase();
if (sizeCalculators[tn]) {
return sizeCalculators[tn](el);
}
else
return [ el.outerWidth(), el.outerHeight() ];
};
jsPlumb.connect({source:circle1.node, target:circle2.node, anchor:"Center", connector:"Straight"});
jsPlumb.connect({source:circle1.node, target:rect.node, anchors:["Center", "Top"], connector:"Straight"});
jsPlumb.connect({source:circle2.node, target:ellipse.node, anchor:"Center"});
//raphael draggable
var start = function () {
this.ox = this.attr("cx");
this.oy = this.attr("cy");
},
move = function (dx, dy) {
this.attr({cx: this.ox + dx, cy: this.oy + dy});
jsPlumb.repaint(this.node);
},
up = function () { };
paper.set(circle1, circle2).drag(move, start, up); });

line charts with different negative colors

Is it possible to have something like this with Jqplot or Google Visualizations
So far I was able to create something similar but not entirely what I want with jqplot
Code:
var style = {
seriesDefaults: {
fill: true,
fillToZero: true,
fillAndStroke: true,
color: "rgba(190,230,110, 0.8)",
fillColor: "rgba(206,236,145, 0.8)",
shadow: false,
lineWidth: 1,
rendererOptions: {
highlightMouseOver: false
}
},
seriesColors: ["#009900", "#000099", "#00cc00", "#0000cc"],
negativeSeriesColors: ["#bb0000", "#ffe700", "#dd0000"] };
You could do something like that in the Google Visualization API, but you would have to calculate the 0-line intersections for the line and add them in as data points, then split your data into two different series (one positive and one negative). These axis crossing points will become part of your data (they will spawn tooltips when you hover over them), but it otherwise meets your requirements:
function drawChart () {
var data = new google.visualization.DataTable();
data.addColumn('number', 'X');
data.addColumn('number', 'Y');
data.addColumn('boolean', 'axis-crossing point');
var y = 0;
for (var x = 0; x < 100; x++) {
y += ~~(Math.random() * 5) * Math.pow(-1, ~~(Math.random() * 2));
if (y < -50) {
y += 5;
}
if (y > 50) {
y -= 5;
}
data.addRow([x, y, false]);
}
// parse the data looking for points where the data crosses the x-axis (at y = 0)
// work backwards because we will be adding new rows to the data set
var p1, p2, m, b, intersect;
for (var i = data.getNumberOfRows() - 1; i > 0; i--) {
p1 = {x: data.getValue(i - 1, 0), y: data.getValue(i - 1, 1)};
p2 = {x: data.getValue(i, 0), y: data.getValue(i, 1)};
if ((p1.y >= 0 && p2.y < 0) || (p1.y < 0 && p2.y >= 0)) {
m = (p2.y - p1.y) / (p2.x - p1.x);
b = p1.y - m * p1.x;
intersect = -1 * b / m;
data.insertRows(i, [
[intersect, p1.y, true],
[intersect, p2.y, true]
]);
}
}
var view = new google.visualization.DataView(data);
view.setColumns([0, {
type: 'number',
label: 'Positive',
calc: function (dt, row) {
var y = dt.getValue(row, 1);
return data.getValue(row, 2) ? 0 : ((y >= 0) ? y : null);
}
}, {
type: 'number',
label: 'Negative',
calc: function (dt, row) {
var y = dt.getValue(row, 1);
return data.getValue(row, 2) ? 0 : ((y < 0) ? y : null);
}
}]);
var chart = new google.visualization.LineChart(document.querySelector('#chart_div'));
chart.draw(view, {
height: 400,
width: 600,
vAxis: {
viewWindow: {
min: -50,
max: 50
}
}
});
}
google.load('visualization', '1', {packages:['corechart'], callback: drawChart});
See working example: http://jsfiddle.net/asgallant/Qc869/