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

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"
}
});

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.

Django Localhost is giving an error for my csv file when using it with d3.js

In my Django application I'm creating a csv file. When I try to use that file with a D3.js boilerplate it can't find the csv file?
I've tried moving where the csv file is to the root and into it's own folder but it doesn't do anything different
{% extends "lm_test/base.html" %}
{% block content %}
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.y.axis path {
display: none;
}
.y.axis line {
stroke: #fff;
stroke-opacity: .2;
shape-rendering: crispEdges;
}
.y.axis .zero line {
stroke: #000;
stroke-opacity: 1;
}
.title {
font: 300 78px Helvetica Neue;
fill: #666;
}
.birthyear,
.age {
text-anchor: middle;
}
.birthyear {
fill: #fff;
}
rect {
fill-opacity: .6;
fill: #e377c2;
}
rect:first-child {
fill: #1f77b4;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 40, bottom: 30, left: 20},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
barWidth = Math.floor(width / 19) - 1;
var x = d3.scale.linear()
.range([barWidth / 2, width - barWidth / 2]);
var y = d3.scale.linear()
.range([height, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")
.tickSize(-width)
.tickFormat(function(d) { return Math.round(d / 1e6) + "M"; });
// An SVG element with a bottom-right origin.
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// A sliding container to hold the bars by birthyear.
var birthyears = svg.append("g")
.attr("class", "birthyears");
// A label for the current year.
var title = svg.append("text")
.attr("class", "title")
.attr("dy", ".71em")
.text(2000);
d3.csv("../temp.csv", function(error, data) {
// Convert strings to numbers.
data.forEach(function(d) {
d.people = +d.people;
d.year = +d.year;
d.age = +d.age;
});
// Compute the extent of the data set in age and years.
var age1 = d3.max(data, function(d) { return d.age; }),
year0 = d3.min(data, function(d) { return d.year; }),
year1 = d3.max(data, function(d) { return d.year; }),
year = year1;
// Update the scale domains.
x.domain([year1 - age1, year1]);
y.domain([0, d3.max(data, function(d) { return d.people; })]);
// Produce a map from year and birthyear to [male, female].
data = d3.nest()
.key(function(d) { return d.year; })
.key(function(d) { return d.year - d.age; })
.rollup(function(v) { return v.map(function(d) { return d.people; }); })
.map(data);
// Add an axis to show the population values.
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ",0)")
.call(yAxis)
.selectAll("g")
.filter(function(value) { return !value; })
.classed("zero", true);
// Add labeled rects for each birthyear (so that no enter or exit is required).
var birthyear = birthyears.selectAll(".birthyear")
.data(d3.range(year0 - age1, year1 + 1, 5))
.enter().append("g")
.attr("class", "birthyear")
.attr("transform", function(birthyear) { return "translate(" + x(birthyear) + ",0)"; });
birthyear.selectAll("rect")
.data(function(birthyear) { return data[year][birthyear] || [0, 0]; })
.enter().append("rect")
.attr("x", -barWidth / 2)
.attr("width", barWidth)
.attr("y", y)
.attr("height", function(value) { return height - y(value); });
// Add labels to show birthyear.
birthyear.append("text")
.attr("y", height - 4)
.text(function(birthyear) { return birthyear; });
// Add labels to show age (separate; not animated).
svg.selectAll(".age")
.data(d3.range(0, age1 + 1, 5))
.enter().append("text")
.attr("class", "age")
.attr("x", function(age) { return x(year - age); })
.attr("y", height + 4)
.attr("dy", ".71em")
.text(function(age) { return age; });
// Allow the arrow keys to change the displayed year.
window.focus();
d3.select(window).on("keydown", function() {
switch (d3.event.keyCode) {
case 37: year = Math.max(year0, year - 10); break;
case 39: year = Math.min(year1, year + 10); break;
}
update();
});
function update() {
if (!(year in data)) return;
title.text(year);
birthyears.transition()
.duration(750)
.attr("transform", "translate(" + (x(year1) - x(year)) + ",0)");
birthyear.selectAll("rect")
.data(function(birthyear) { return data[year][birthyear] || [0, 0]; })
.transition()
.duration(750)
.attr("y", y)
.attr("height", function(value) { return height - y(value); });
}
});
</script>
{% endblock content %}
The view is:
def results(request):
disease = request.GET.get('disease_name')
year_from = int(request.GET.get('year_from'))
year_to = int(request.GET.get('year_to'))
if year_from < year_to:
years = range(year_from, year_to, +1)
else:
years = range(year_from, year_to, -1)
Entrez.email = "chrisgbeldam#gmail.com" #Required by NCBI
results_file = open('temp.csv', 'w') #Open csv file
result_writer = csv.writer(results_file, delimiter=',')
result_writer.writerow(['Year', 'Number Of Results'])
for year in years: #Checks the number of results for each year and then loops
handle = Entrez.esearch(
db="pubmed",
sort="relevance",
term=disease,
mindate=year,
maxdate=year,
retmode="xml",
)
results = Entrez.read(handle)
results_count = results['Count'] # Total number of results for the search
results_yearly = print(f"Number of papers in {year} is {results_count}")
handle.close() #Close E Search
result_writer.writerow([year,results_count]) # Writes out the results to csv file
results_file.close()
context = {
'disease': disease,
'year': year,
'results': results,
'results_count': results_count,
'results_file': results_file,
}
return render(request, 'lm_test/results.html', context)
I expect the outcome to be that D3 can see my temp.csv file but it refuses to and gives me a 'Failed to load resource: the server responded with a status of 404 (Not Found)' error and also a Uncaught TypeError: Cannot read property 'forEach' of undefined
at Object. (?csrfmiddlewaretoken=4I4wgoSynXQX6FyeSsDJWgNnjH6CgQyoZ0U7pvJV0D8vEBRynwYSalwrCZPjtw7v&disease_name=Cancer&year_from=2016&year_to=2000&submit=:117)
at Object.t (d3.v3.min.js:1)
at XMLHttpRequest.i (d3.v3.min.js:1)' error
Seem to work properly after closing and rerunning the virtual environment! Odd

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

How to clip a view in 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;
};

Raphael - Drawing shapes with mouse

What would be the best way to draw a rectangle or circle for that matter using your mouse ?
I just started looking at raphael and it seems I should use, drag? Or mousedown and mouseup ?
No sure.
I would use mousedown and mouseup events on your canvas/paper. On mouse down you should store the x and y positions of the mousedown, then on mouseup you should use the current mouse positions to calculate width and height.
Here is an example, bear in mind thought that I am using JQuery to calculate the mouse positions (if this is not available for you, then you should find another way to get the mouse offsets)
//global variables
var mouseDownX = 0;
var mouseDownY = 0;
//register events on document load
paper.mousedown(OnMouseDown);
paper.mouseup(OnMouseUp);
function OnMouseDown(e){
var offset = $("#Canvas").offset();//This is JQuery function
mouseDownX = e.pageX - offset.left;
mouseDownY = e.pageY - offset.top;
}
function OnMouseUp(e){
var offset = $("#Canvas").offset();//This is JQuery function
var upX = e.pageX - offset.left;
var upY = e.pageY - offset.top;
var width = upX - mouseDownX;
var height = upY - mouseDownY;
DrawRectangle(mouseDownX, mouseDownY, width, height);
}
function DrawRectangle(x, y, w, h){
var element = paper.rect(x, y, w, h);
element.attr({
fill: "#FFF",
stroke: "#F00"
});
}
Hope that helps
Here's an updated version of fiddle (mentioned in the other article) that works with Raphael 2+ (no paper events).
This fiddle shows how a rectangle is dynamically drawn while you drag the mouse.
var mouseDownX = 0;
var mouseDownY = 0;
var elemClicked;
var rect;
$(document).ready(function() {
var paper = Raphael("d1", 300, 200);
// start, move, and up are the drag functions
start = function() {
// storing original coordinates
this.ox = this.attr("x");
this.oy = this.attr("y");
this.attr({
opacity: 1
});
if (this.attr("y") < 60 && this.attr("x") < 60) this.attr({
fill: "#000"
});
}, move = function(dx, dy) {
// move will be called with dx and dy
if (this.attr("y") > 200 || this.attr("x") > 300) this.attr({
x: this.ox + dx,
y: this.oy + dy
});
else {
nowX = Math.min(300, this.ox + dx);
nowY = Math.min(200, this.oy + dy);
nowX = Math.max(0, nowX);
nowY = Math.max(0, nowY);
this.attr({
x: nowX,
y: nowY
});
if (this.attr("fill") != "#000") this.attr({
fill: "#000"
});
}
}, up = function() {
// restoring state
this.attr({
opacity: .5
});
if (this.attr("y") < 60 && this.attr("x") < 60) this.attr({
fill: "#AEAEAE"
});
};
function DrawRectangle(x, y, w, h) {
var element = paper.rect(x, y, w, h);
element.attr({
fill: "gray",
opacity: .5,
stroke: "#F00"
});
$(element.node).attr('id', 'rct' + x + y);
console.log(element.attr('x'));
element.drag(move, start, up);
element.click(function(e) {
elemClicked = $(element.node).attr('id');
});
return element;
}
$("#bind").click(function(e) {
$('#d1').unbind('mousedown');
$('#d1').unbind('mousemove');
$('#d1').unbind('mouseup');
$("#d1").mousedown(function(e) {
// Prevent text edit cursor while dragging in webkit browsers
e.originalEvent.preventDefault();
var offset = $("#d1").offset();
mouseDownX = e.pageX - offset.left;
mouseDownY = e.pageY - offset.top;
rect = DrawRectangle(mouseDownX, mouseDownY, 0, 0);
$("#d1").mousemove(function(e) {
var offset = $("#d1").offset();
var upX = e.pageX - offset.left;
var upY = e.pageY - offset.top;
var width = upX - mouseDownX;
var height = upY - mouseDownY;
rect.attr( { "width": width > 0 ? width : 0,
"height": height > 0 ? height : 0 } );
});
});
$("#d1").mouseup(function(e) {
$('#d1').unbind('mousemove');
var BBox = rect.getBBox();
if ( BBox.width == 0 && BBox.height == 0 ) rect.remove();
});
});
$("#unbind").click(function(e) {
$('#d1').unbind('mouseup');
$('#d1').unbind('mousemove');
$('#d1').unbind('mousedown');
});
$("#clr").click(function(e) {
$('#d1').find('rect').each(function(i, obj) {
$(this).remove();
});
});
$("#del").click(function(e) {
$('#' + elemClicked).remove();
});
});
​
I have tried to modify the above fiddle from Miro. Please check my updated version here
var canvas = $('#canvas');
var paper = Raphael(canvas.attr('id'), canvas.width(), canvas.height());
var mouseDownX = 0;
var mouseDownY = 0;
var elemClicked;
var shap;
var borderColor = '#093';
var fillColor = '#eee';
var option = 1;
var shapOpacity = .5;
var ShapType = {RECTANGLE: 1, CIRCLE: 2, LINE: 3}
$(document).ready(function() {
$("#action").change(function() {
option = $('option:selected', this).val();
});
$("#start").click(function(e) {
reset();
$(canvas).mousedown(function(e) {
e.originalEvent.preventDefault();
var offset = $(canvas).offset();
mouseDownX = e.pageX - offset.left;
mouseDownY = e.pageY - offset.top;
if(option == ShapType.RECTANGLE){
shap = drawRectangle(mouseDownX, mouseDownY, 0, 0);
}
else if(option == ShapType.CIRCLE){
shap = drawCircle(mouseDownX, mouseDownY, mouseDownX, mouseDownY);
}else if(option == ShapType.LINE){
shap = drawLine(mouseDownX, mouseDownY, mouseDownX, mouseDownY);
}
$(canvas).mousemove(function(e) {
var offset = $(canvas).offset();
var upX = e.pageX - offset.left;
var upY = e.pageY - offset.top;
var width = upX - mouseDownX;
var height = upY - mouseDownY;
if(option == ShapType.RECTANGLE){
shap.attr( { "width": width > 0 ? width : 0,
"height": height > 0 ? height : 0 } );
}else if(option == ShapType.CIRCLE || option == ShapType.LINE){
shap.updateEnd(upX, upY);
}
}); // end mouse down.
});// end mouse down.
$(canvas).mouseup(function(e) {
$(canvas).unbind('mousemove');
if(option == ShapType.RECTANGLE){
var BBox = shap.getBBox();
if ( BBox.width == 0 && BBox.height == 0 ) shap.remove();
}
}); // end mouse up.
}); // end document ready.
$("#done").click(function(e) {
$(canvas).unbind('mouseup');
$(canvas).unbind('mousemove');
$(canvas).unbind('mousedown');
});
$("#clear").click(function(e) {
$(canvas).find('rect, circle, path').each(function(i, obj) {
$(this).remove();
});
});
$("#deleteshap").click(function(e) {
$('#' + elemClicked).remove();
});
var start = function() {
this.ox = this.attr("x");
this.oy = this.attr("y");
this.attr({
opacity: shapOpacity
});
this.ow = this.attr('width');
this.oh = this.attr('height');
}
var move = function(dx, dy) {
nowX = Math.min(paper.width, this.ox + dx);
nowY = Math.min(paper.height, this.oy + dy);
nowX = Math.max(0, nowX);
nowY = Math.max(0, nowY);
this.attr({
x: nowX,
y: nowY
});
if (this.attr("fill") != fillColor) this.attr({
fill: fillColor
});
}
var up = function() {
this.attr({
opacity: shapOpacity
});
if (this.attr("y") < 60 && this.attr("x") < 60) this.attr({
fill: fillColor
});
};
var reset = function(){
$(canvas).unbind('mousedown');
$(canvas).unbind('mousemove');
$(canvas).unbind('mouseup');
}
function drawLine(startX, startY, endX, endY) {
var start = {
x: startX,
y: startY
};
var end = {
x: endX,
y: endY
};
var getPath = function() {
return "M" + start.x + " " + start.y + " L" + end.x + " " + end.y;
};
var redraw = function() {
node.attr("path", getPath());
node.attr({
stroke: borderColor
});
}
var node = paper.path(getPath());
$(node.node).attr('id', 'shap' + startX + startY);
node.click(function(e) {
elemClicked = $(node.node).attr('id');
});
return {
updateStart: function(x, y) {
start.x = x;
start.y = y;
redraw();
return this;
},
updateEnd: function(x, y) {
end.x = x;
end.y = y;
redraw();
return this;
}
};
};
function drawRectangle(x, y, w, h) {
var element = paper.rect(x, y, w, h);
element.attr({
fill: fillColor,
opacity: shapOpacity,
stroke: borderColor
});
$(element.node).attr('id', 'shap' + x + y);
element.drag(move, start, up);
element.click(function(e) {
elemClicked = $(element.node).attr('id');
});
return element;
}
function drawCircle(x1, y1, x2, y2)
{
var center = {
x: (x1+x2)/2,
y: (y1+y2)/2
};
var radius = {
h: Math.sqrt((y2 - y1) * (y2 - y1))/2,
w: Math.sqrt((x2 - x1) * (x2 - x1))/2
};
var getPath = function () {
return [["M", center.x, center.y], ["m", 0, -radius.h],
["a", radius.w, radius.h, 0, 1, 1, 0, 2 * radius.h],
["a", radius.w, radius.h, 0, 1, 1, 0, -2 * radius.h],
["z"]];
};
var redraw = function () {
node.attr("path", getPath());
node.attr(
{ fill: fillColor,
stroke: borderColor,
});
};
var node = paper.path(getPath());
$(node.node).attr('id', 'shap' + x1 + y1);
node.click(function(e) {
elemClicked = $(node.node).attr('id');
});
dragCircle(node);
return {
updateStart: function (x, y) {
center.x = (x1 + x) / 2;
center.y = (y1 + y) / 2;
radius.w = Math.sqrt((x - x1) * (x - x1))/2;
radius.h = Math.sqrt((y - y1) * (y - y1))/2;
redraw();
return this;
},
updateEnd: function (x, y) {
center.x = (x1 + x) / 2;
center.y = (y1 + y) / 2;
radius.w = Math.sqrt((x - x1) * (x - x1))/2;
radius.h = Math.sqrt((y - y1) * (y - y1))/2;
redraw();
return this;
}
};
} // end circle
dragCircle = function(node) {
var me = node,
lx = 0,
ly = 0,
ox = 0,
oy = 0,
moveFnc = function(dx, dy) {
lx = dx + ox;
ly = dy + oy;
me.transform('t' + lx + ',' + ly);
},
startFun = function() {},
endFnc = function() {
ox = lx;
oy = ly;
};
node.drag(moveFnc, function() {} , endFnc);
};
});
#canvas{background-color:#eee; background-size:cover; width:400px; height:300px;}
.controls input{background: #15A3D7;border: #eee 1px solid;border-radius: 3px;padding: 3px 12px;margin: 5px 3px;color: #fff;}
.controls select{padding: 3px;background: #eee;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="controls">
<input id="start" value="Start" type="button" />
<input id="done" value="Done" type="button" />
<input id="deleteshap" value="Delete Shap" type="button" />
<input id="clear" value="Clear All" type="button" />
<select id="action">
<option value="1">Rectangle</option>
<option value="2">Circle</option>
<option value="3">Line</option>
</select>
</div>
<div class="dvcls" id="canvas"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
Here is also a good reference to play with Raphael Js.
http://www.ebooksbucket.com/instant-raphaeljs-starter-b184
Hope this will help.