I tried to smoothly move div(circle), but I can not do it. Div immediately moves to the last point.
I tried to simulate the process of the ball falling.
I used method animate with second param 0, but this did not help me.
How to do it?
"use strict";
function calculateH(h, t) {
return h - ((Math.pow(t, 2)*9.8)/2);
}
/**
* [getTrip function is calculate Y coordinates for the ball thrown down]
* #param {[number]} h [The height from which the ball falls]
* #param {[number]} t [Time elapsed from the beginning of the fall of the ball]
* #param {[number]} interval [EPS]
* #param {[number]} k [Ratio of height to screen height. It is necessary that the ball fell to the site bottom]
* #return {[array]} [Array of Y coordinates. {0, 0.2, 1.2 ... h}]
*/
function getTrip(h, t, interval, k) {
var calculations = new Array();
for(t; calculateH(h, t) > 0; t += interval)
calculations.push((h - calculateH(h, t))*k);
return calculations;
}
$('document').ready(function() {
var bol = $('#mycircle');
var h = 100;
var t = 0;
var interval = 0.001; // eps
/**
* [k is the ratio of height of the screen to start the ball drop height]
* #type {[number]}
*/
var k = ($(document).height()-bol.height()) / h;
var calculations = getTrip(h, t, interval, k);
// Problem is there.
// I want animate of fell ball, but this code just move in last Y coord.
calculations.forEach(function(y) {
bol.css({'margin-top': y+'px'});
});
bol.animate({'margin-top': h*k+'px'}, 1); // prees to the bottom
});
https://jsfiddle.net/82agzc2e/4/
Why are you using a loop, and not animate the margin-top directly to last position?
bol.animate({'margin-top': calculations[calculations.length - 1]+'px'}, 1000);
Working example.
Related
What my game has is a character in a preset position in a 2d-game mobile(x=0.33, y=9.48, z=0). I have enemys that are spawn from different angles of the screen with a movement script. But i have to modify the script so it moves towards the character position always.
Tried to set the position of the enemy = with character position. but that doesnt work.
Anyone has any idea how can i do that?
here is the script:
///
//Speed - essential : true ?
let speed;
let enabled = false;
let phys;
function init() {
speed =4;
//let d = Math.sqrt( Math.pow((enemy.x-playerPos.x), 2) + Math.pow((enemy.y-playerPos.y), 2) );
}
function update(dt) {
dt = 1 / 60.0; // fixed delta time
let enemy = this.entity().position();
let player = this.scene().find('Actor')[0];
let playerPos = player.worldPosition();
let d = new Vec3(
playerPos.x - enemy.x,
playerPos.y - enemy.y,
playerPos.z - enemy.z
)
const length = Math.sqrt(d.x * d.x + d.y * d.y);
let dirTowardsPlayer = new Vec3 (
d.x / length,
d.y / length,
d.z / length
)
this.entity().setPosition(
dt * dirTowardsPlayer.x * speed,
dt * dirTowardsPlayer.y * speed,
dt * dirTowardsPlayer.z * speed);
log(dirTowardsPlayer)
}
function signal(name, value) {
enabled = value;
}
i am expecting the enemy to move towards the character preseted position
I have one circle, which grows and shrinks by manipulating the radius in a loop.
While growing and shrinking, I draw a point on that circle. And within the same loop, increasing the angle for a next point.
The setup is like this:
let radius = 0;
let circleAngle = 0;
let radiusAngle = 0;
let speed = 0.02;
let radiusSpeed = 4;
let circleSpeed = 2;
And in the loop:
radius = Math.cos(radiusAngle) * 100;
// creating new point for line
let pointOnCircle = {
x: midX + Math.cos(circleAngle) * radius,
y: midY + Math.sin(circleAngle) * radius
};
circleAngle += speed * circleSpeed;
radiusAngle += speed * radiusSpeed;
This produces some kind of flower / pattern to be drawn.
After unknown rotations, the drawing line connects to the point from where it started, closing the path perfectly.
Now I would like to know how many rotations must occure, before the line is back to it's beginning.
A working example can be found here:
http://codepen.io/anon/pen/RGKOjP
The console logs the current rotations of both the circle and the line.
Full cycle is over, when both radius and point returns to the starting point. So
speed * circleSpeed * K = 360 * N
speed * radiusSpeed * K = 360 * M
Here K is unknown number of turns, N and M are integer numbers.
Divide the first equation by the second
circleSpeed / radiusSpeed = N / M
If speed values are integers, divide them by LCM to get minimal valid N and M values, if they are rational, multiply them to get integer proportion.
For your example minimal integers N=1,M=2, so we can get
K = 360 * 1 / (0.02 * 2) = 9000 loop turns
I have been asked by a friend to add a high score table to a game where you maneuver a snake that gets larger as it eats food. My friend wanted me to add a high score to the game.
here is the original code;
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple Snake Game</title>
<!-- Basic styling, centering of the canvas. -->
<style>
canvas {
display: block;
position: absolute;
border: 1px solid #000;
margin: auto;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
</style>
</head>
<body>
<script>
var
/**
* Constats
*/
COLS = 26,
ROWS = 26,
EMPTY = 0,
SNAKE = 1,
FRUIT = 2,
LEFT = 0,
UP = 1,
RIGHT = 2,
DOWN = 3,
KEY_LEFT = 37,
KEY_UP = 38,
KEY_RIGHT = 39,
KEY_DOWN = 40,
/**
* Game objects
*/
canvas, /* HTMLCanvas */
ctx, /* CanvasRenderingContext2d */
keystate, /* Object, used for keyboard inputs */
frames, /* number, used for animation */
score; /* number, keep track of the player score */
high_score = [];
/**
* Grid datastructor, usefull in games where the game world is
* confined in absolute sized chunks of data or information.
*
* #type {Object}
*/
grid = {
width: null, /* number, the number of columns */
height: null, /* number, the number of rows */
_grid: null, /* Array<any>, data representation */
/**
* Initiate and fill a c x r grid with the value of d
* #param {any} d default value to fill with
* #param {number} c number of columns
* #param {number} r number of rows
*/
init: function(d, c, r) {
this.width = c;
this.height = r;
this._grid = [];
for (var x=0; x < c; x++) {
this._grid.push([]);
for (var y=0; y < r; y++) {
this._grid[x].push(d);
}
}
},
/**
* Set the value of the grid cell at (x, y)
*
* #param {any} val what to set
* #param {number} x the x-coordinate
* #param {number} y the y-coordinate
*/
set: function(val, x, y) {
this._grid[x][y] = val;
},
/**
* Get the value of the cell at (x, y)
*
* #param {number} x the x-coordinate
* #param {number} y the y-coordinate
* #return {any} the value at the cell
*/
get: function(x, y) {
return this._grid[x][y];
}
}
/**
* The snake, works as a queue (FIFO, first in first out) of data
* with all the current positions in the grid with the snake id
*
* #type {Object}
*/
snake = {
direction: null, /* number, the direction */
last: null, /* Object, pointer to the last element in
the queue */
_queue: null, /* Array<number>, data representation*/
/**
* Clears the queue and sets the start position and direction
*
* #param {number} d start direction
* #param {number} x start x-coordinate
* #param {number} y start y-coordinate
*/
init: function(d, x, y) {
this.direction = d;
this._queue = [];
this.insert(x, y);
},
/**
* Adds an element to the queue
*
* #param {number} x x-coordinate
* #param {number} y y-coordinate
*/
insert: function(x, y) {
// unshift prepends an element to an array
this._queue.unshift({x:x, y:y});
this.last = this._queue[0];
},
/**
* Removes and returns the first element in the queue.
*
* #return {Object} the first element
*/
remove: function() {
// pop returns the last element of an array
return this._queue.pop();
}
};
/**
* Set a food id at a random free cell in the grid
*/
function setFood() {
var empty = [];
// iterate through the grid and find all empty cells
for (var x=0; x < grid.width; x++) {
for (var y=0; y < grid.height; y++) {
if (grid.get(x, y) === EMPTY) {
empty.push({x:x, y:y});
}
}
}
// chooses a random cell
var randpos = empty[Math.round(Math.random()*(empty.length - 1))];
grid.set(FRUIT, randpos.x, randpos.y);
}
/**
* Starts the game
*/
function main() {
// create and initiate the canvas element
canvas = document.createElement("canvas");
canvas.width = COLS*20;
canvas.height = ROWS*20;
ctx = canvas.getContext("2d");
// add the canvas element to the body of the document
document.body.appendChild(canvas);
// sets an base font for bigger score display
ctx.font = "12px Helvetica";
frames = 0;
keystate = {};
// keeps track of the keybourd input
document.addEventListener("keydown", function(evt) {
keystate[evt.keyCode] = true;
});
document.addEventListener("keyup", function(evt) {
delete keystate[evt.keyCode];
});
// intatiate game objects and starts the game loop
init();
loop();
}
/**
* Resets and inits game objects
*/
function init() {
score = 0;
grid.init(EMPTY, COLS, ROWS);
var sp = {x:Math.floor(COLS/2), y:ROWS-1};
snake.init(UP, sp.x, sp.y);
grid.set(SNAKE, sp.x, sp.y);
setFood();
}
/**
* The game loop function, used for game updates and rendering
*/
function loop() {
update();
draw();
// When ready to redraw the canvas call the loop function
// first. Runs about 60 frames a second
window.requestAnimationFrame(loop, canvas);
}
/**
* Updates the game logic
*/
function update() {
frames++;
// changing direction of the snake depending on which keys
// that are pressed
if (keystate[KEY_LEFT] && snake.direction !== RIGHT) {
snake.direction = LEFT;
}
if (keystate[KEY_UP] && snake.direction !== DOWN) {
snake.direction = UP;
}
if (keystate[KEY_RIGHT] && snake.direction !== LEFT) {
snake.direction = RIGHT;
}
if (keystate[KEY_DOWN] && snake.direction !== UP) {
snake.direction = DOWN;
}
// each five frames update the game state.
if (frames%5 === 0) {
// pop the last element from the snake queue i.e. the
// head
var nx = snake.last.x;
var ny = snake.last.y;
// updates the position depending on the snake direction
switch (snake.direction) {
case LEFT:
nx--;
break;
case UP:
ny--;
break;
case RIGHT:
nx++;
break;
case DOWN:
ny++;
break;
}
// checks all gameover conditions
if (0 > nx || nx > grid.width-1 ||
0 > ny || ny > grid.height-1 ||
grid.get(nx, ny) === SNAKE
) {
return init();
}
// check wheter the new position are on the fruit item
if (grid.get(nx, ny) === FRUIT) {
// increment the score and sets a new fruit position
score++;
setFood();
} else {
// take out the first item from the snake queue i.e
// the tail and remove id from grid
var tail = snake.remove();
grid.set(EMPTY, tail.x, tail.y);
}
// add a snake id at the new position and append it to
// the snake queue
grid.set(SNAKE, nx, ny);
snake.insert(nx, ny);
}
}
/**
* Render the grid to the canvas.
*/
function draw() {
// calculate tile-width and -height
var tw = canvas.width/grid.width;
var th = canvas.height/grid.height;
// iterate through the grid and draw all cells
for (var x=0; x < grid.width; x++) {
for (var y=0; y < grid.height; y++) {
// sets the fillstyle depending on the id of
// each cell
switch (grid.get(x, y)) {
case EMPTY:
ctx.fillStyle = "#fff";
break;
case SNAKE:
ctx.fillStyle = "#0ff";
break;
case FRUIT:
ctx.fillStyle = "#f00";
break;
}
ctx.fillRect(x*tw, y*th, tw, th);
}
}
// changes the fillstyle once more and draws the score
// message to the canvas
ctx.fillStyle = "#000";
ctx.fillText("SCORE: " + score, 10, canvas.height-10);
}
// start and run the game
main();
</script>
</body>
</html>
EDI: here is where I'm having problems
I added a high score variable here:
/**
* Game objects
high_scores = []; // new code */
canvas, /* HTMLCanvas */
ctx, /* CanvasRenderingContext2d */
keystate, /* Object, used for keyboard inputs */
frames, /* number, used for animation */
score, /* number, keep track of the player score */
As well as here:
} else {
// take out the first item from the snake queue i.e
// the tail and remove id from grid
var tail = snake.remove();
grid.set(EMPTY, tail.x, tail.y);
high_scores = high_scores.push(score)
}
i don't get how a couple of javascript changes make the layout disappear. I've never coded in javascript that much and I dont understand why javaScript changes are affecting the layout.
I found a solution. there was an error in my syntax here
/**
* Game objects
*/
canvas, /* HTMLCanvas */
ctx, /* CanvasRenderingContext2d */
keystate, /* Object, used for keyboard inputs */
frames, /* number, used for animation */
score, /* number, keep track of the player score */
high_scores = [];
I was working on a fun project that implicates creating "imperfect" circles by drawing them with lines and animate their points to generate a pleasing effect.
The points should alternate between moving away and closer to the center of the circle, to illustrate:
I think I was able to accomplish that, the problem is when I try to render it in a canvas half the render jitters like crazy, you can see it in this demo.
You can see how it renders for me in this video. If you pay close attention the bottom right half of the render runs smoothly while the top left just..doesn't.
This is how I create the points:
for (var i = 0; i < q; i++) {
var a = toRad(aDiv * i);
var e = rand(this.e, 1);
var x = Math.cos(a) * (this.r * e) + this.x;
var y = Math.sin(a) * (this.r * e) + this.y;
this.points.push({
x: x,
y: y,
initX: x,
initY: y,
reverseX: false,
reverseY: false,
finalX: x + 5 * Math.cos(a),
finalY: y + 5 * Math.sin(a)
});
}
Each point in the imperfect circle is calculated using an angle and a random distance that it's not particularly relevant (it relies on a few parameters).
I think it's starts to mess up when I assign the final values (finalX,finalY), the animation is supposed to alternate between those and their initial values, but only half of the render accomplishes it.
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I can't figure it out, thanks in advance!
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I Think that your animation function has not care about the elapsed time. Simply the animation occurs very fast. The number of requestAnimationFrame callbacks is usually 60 times per second, So Happens just what is expected to happen.
I made some fixes in this fiddle. This animate function take care about timestamp. Also I made a gradient in the animation to alternate between their final and initial positions smoothly.
ImperfectCircle.prototype.animate = function (timestamp) {
var factor = 4;
var stepTime = 400;
for (var i = 0, l = this.points.length; i < l; i++) {
var point = this.points[i];
var direction = Math.floor(timestamp/stepTime)%2;
var stepProgress = timestamp % stepTime * 100 / stepTime;
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
}
}
Step by Step:
based on comments
// 1. Calculates the steps as int: Math.floor(timestamp/stepTime)
// 2. Modulo to know if even step or odd step: %2
var direction = Math.floor(timestamp/stepTime)%2;
// 1. Calculates the step progress: timestamp % stepTime
// 2. Convert it to a percentage: * 100 / stepTime
var stepProgress = timestamp % stepTime * 100 / stepTime;
// if odd invert the percentage.
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
// recompute position based on step percentage
// factor is for fine adjustment.
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I am trying to do some complicated effect, and to do it i have to break it down into its components, upon which i can build on and hopefully they will come together.
Now to make a circle in canvas is easy. But i want to make it myself. So I want to write a function that would be given a point that's center, radius, and then it will draw a circle with 1 px stroke width.
How would i go about it? If i look at from math perspective, what comes to mind is use circle distance formula and increment by small values, like .3 degrees, and make a dot at circumference. But if my circle is too small, like 2 px radius. Then it will waste lot of time drawing that won't matter, and if it's big enough you will see spaces between dots.
so i want my circle drawing function to draw a
dot if radius is 1px.
4 dots around the center if radius is 2px.
..and so on.
also if this gonna make my circle look rigid, i want there to be antialiasing too :D
I suppose once i know how to make outline filling it in won't be a problem..all i'd've to do is reduce the radius and keep drawing until radius is 1px.
You have the center x0, y0 and the radius r. Basically you need the parametric equation of circle:
x = x0 + r * cos(t)
y = y0 + r * sin(t)
Where t is the angle between a radial segment and normalized x-axis, and you need to divide it up as needed. For example for your four points case you do
360/4 = 90
and so use 0, 90, 180, 270 to get the four points.
OK, I've re-factored my earlier code as a jQuery plugin named "canvasLens". It accepts a bunch of options to control things like image src, lens size and border color. You can even choose between two different lens effects, "fisheye" or "scaledSquare".
I've tried to make it as self-explanatory as possible with a header block and plenty of other comments.
/*
* Copyright (c) 2014 Roamer-1888
* "canvasLens"
* a jQuery plugin for a lens effect on one or more HTML5 canvases
* by Roamer-1888, 2014-11-09
* http://stackoverflow.com/users/3478010/roamer-1888
*
* Written in response to aa question by Muhammad Umer, here
* http://stackoverflow.com/questions/26793321/
*
* Invoke on a canvas element as follows
* $("#canvas").lens({
* imgSrc: 'path/to/image',
* imgCrossOrigin: '' | 'anonymous' | 'use-credentials', //[1]
* drawImageCoords: [ //[2]
* 0, 0, //(sx,st) Source image sub-rectangle Left,Top.
* 1350, 788, //(sw/sh) Source image sub-rectangle Width,Height.
* 0, 0, //(dx/dy) Destination Left,Top.
* 800, 467 //(dw/dh) Destination image sub-rectangle Width,Height.
* ],
* effect: 'fisheye' | 'scaledSquare',
* scale: 2 //currently affects only 'scaledSquare'
* size: 100, //diameter/side-length of the lens in pixels
* hideCursor: true | false,
* border: [0, 0, 0, 255] //[r,g,b,alpha] (base-10) | 'none'
* });
*
* Demo: http://jsfiddle.net/7z6by3o3/1/
*
* Further reading :
* [1] imgCrossOrigin -
* https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes
* [2] drawImageCoords -
* https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D
*
* Licence: MIT - http://en.wikipedia.org/wiki/MIT_License
*
* Please keep this header block intact, with amendments
* to reflect any changes made to the code.
*
*/
(function($){
// *******************************
// ***** Start: Private vars *****
// *******************************
var pluginName = 'canvasLens';
// *****************************
// ***** Fin: Private vars *****
// *****************************
// **********************************
// ***** Start: Private Methods *****
// **********************************
// Note that in all private methods,
// `this` is the canvas on which
// the plugin is invoked.
// Most private methods are called
// with `methodName.call(this)`.
// **********************************
function animate() {
var data = $(this).data(pluginName);
if(data) {
draw.call(this);
requestAnimationFrame(animate.bind(this));
}
}
function draw() {
var data = $(this).data(pluginName);
data.ctx.drawImage(data.m_can, 0, 0);
if(data.showLens) {
if(data.settings.effect == 'scaledSquare') {
scaledSquare.call(this);
} else {
fisheye.call(this);
}
}
}
function putBg() {
var data = $(this).data(pluginName);
data.m_ctx.drawImage.apply(data.m_ctx, [data.img].concat(data.settings.drawImageCoords));
}
function scaledSquare() {
var data = $(this).data(pluginName),
xt = data.settings.scale,
h = data.settings.size;
data.ctx.drawImage(data.m_can,
data.mouse.x - h/xt/2, data.mouse.y - h/xt/2, //sx,st Source image sub-rectangle Left,Top coordinates.
h/xt, h/xt, //sw/sh Source image sub-rectangle Width,Height.
data.mouse.x - h/2, data.mouse.y - h/2, //dx/dy Destination Left,Top coordinates.
h, h //dw/dh The Width,Height to draw the image in the destination canvas.
);
}
function fisheye() {
var data = $(this).data(pluginName),
d = data.settings.size,
mx = data.mouse.x, my = data.mouse.y,
srcpixels = data.m_ctx.getImageData(mx - d/2, my - d/2, d, d);
fisheyeTransform.call(this, srcpixels.data, data.xpixels.data, d, d);
data.ctx.putImageData(data.xpixels, mx - d/2, my - d/2);
}
function fisheyeTransform(srcData, xData, w, h) {
/*
* Fish eye effect (barrel distortion)
* *** adapted from ***
* tejopa, 2012-04-29
* http://popscan.blogspot.co.ke/2012/04/fisheye-lens-equation-simple-fisheye.html
*/
var data = $(this).data(pluginName),
y, x, ny, nx, ny2, nx2, r, nr, theta, nxn, nyn, x2, y2, pos, srcpos;
for (var y=0; y<h; y++) { // for each row
var ny = ((2 * y) / h) - 1; // normalize y coordinate to -1 ... 1
ny2 = ny * ny; // pre calculate ny*ny
for (x=0; x<w; x++) { // for each column
pos = 4 * (y * w + x);
nx = ((2 * x) / w) - 1; // normalize x coordinate to -1 ... 1
nx2 = nx * nx; // pre calculate nx*nx
r = Math.sqrt(nx2 + ny2); // calculate distance from center (0,0)
if(r > 1) {
/* 1-to-1 pixel mapping outside the circle */
/* An improvement would be to make this area transparent. ?How? */
xData[pos+0] = srcData[pos+0];//red
xData[pos+1] = srcData[pos+1];//green
xData[pos+2] = srcData[pos+2];//blue
xData[pos+3] = srcData[pos+3];//alpha
}
else if(data.settings.border && data.settings.border !== 'none' && r > (1-3/w) && r < 1) { // circular border around fisheye
xData[pos+0] = data.settings.border[0];//red
xData[pos+1] = data.settings.border[1];//green
xData[pos+2] = data.settings.border[2];//blue
xData[pos+3] = data.settings.border[3];//alpha
}
else if (0<=r && r<=1) { // we are inside the circle, let's do a fisheye transform on this pixel
nr = Math.sqrt(1 - Math.pow(r,2));
nr = (r + (1 - nr)) / 2; // new distance is between 0 ... 1
if (nr<=1) { // discard radius greater than 1.0
theta = Math.atan2(ny, nx); // calculate the angle for polar coordinates
nxn = nr * Math.cos(theta); // calculate new x position with new distance in same angle
nyn = nr * Math.sin(theta); // calculate new y position with new distance in same angle
x2 = Math.floor(((nxn + 1) * w) / 2); // map from -1 ... 1 to image coordinates
y2 = Math.floor(((nyn + 1) * h) / 2); // map from -1 ... 1 to image coordinates
srcpos = Math.floor(4 * (y2 * w + x2));
if (pos >= 0 && srcpos >= 0 && (pos+3) < xData.length && (srcpos+3) < srcData.length) { // make sure that position stays within arrays
/* get new pixel (x2,y2) and put it to target array at (x,y) */
xData[pos+0] = srcData[srcpos+0];//red
xData[pos+1] = srcData[srcpos+1];//green
xData[pos+2] = srcData[srcpos+2];//blue
xData[pos+3] = srcData[srcpos+3];//alpha
}
}
}
}
}
}
// ********************************
// ***** Fin: Private methods *****
// ********************************
// *********************************
// ***** Start: Public Methods *****
// *********************************
var methods = {
'init': function(options) {
//"this" is a jquery object on which this plugin has been invoked.
return this.each(function(index) {
var can = this,
$this = $(this);
var data = $this.data(pluginName);
if (!data) { // If the plugin hasn't been initialized yet
data = {
target: $this,
showLens: false,
mouse: {x:0, y:0}
};
$this.data(pluginName, data);
var settings = {
imgSrc: '',
imgCrossOrigin: '',
drawImageCoords: [
0, 0, //sx,st Source image sub-rectangle Left,Top coordinates.
500, 500, //sw/sh Source image sub-rectangle Width,Height.
0, 0, //dx/dy Destination Left,Top coordinates.
500, 500 //(dw/dh) Destination image sub-rectangle Width,Height.
],
effect: 'fisheye',
scale: 2,
size: 100,
border: [0, 0, 0, 255], //[r,g,b,alpha] base-10
hideCursor: false
};
if(options) {
$.extend(true, settings, options);
}
data.settings = settings;
if(settings.hideCursor) {
data.originalCursor = $this.css('cursor');
$this.css('cursor', 'none');
}
$this.on('mouseenter.'+pluginName, function(e) {
data.showLens = true;
}).on('mousemove.'+pluginName, function(e) {
data.mouse.x = e.offsetX;
data.mouse.y = e.offsetY;
}).on('mouseleave.'+pluginName, function(e) {
data.showLens = false;
});
data.m_can = $("<canvas>").attr({
'width': can.width,
'height': can.height
})[0];
data.ctx = can.getContext("2d"); // lens effect
data.m_ctx = data.m_can.getContext('2d'); // background image
data.xpixels = data.ctx.getImageData(0, 0, settings.size, settings.size);
data.img = new Image();
data.img.onload = function() {
putBg.call(can);
animate.call(can);
};
data.img.crossOrigin = settings.imgCrossOrigin;
data.img.src = settings.imgSrc;
}
});
},
'destroy': function() {
return this.each(function(index) {
var $this = $(this),
data = $this.data(pluginName);
$this.off('mouseenter.'+pluginName)
.off('mousemove.'+pluginName)
.off('mouseleave.'+pluginName);
if(data && data.originalCursor) {
$this.css('cursor', data.originalCursor);
}
$this.data(pluginName, null);
});
}
};
// *******************************
// ***** Fin: Public Methods *****
// *******************************
// *****************************
// ***** Start: Supervisor *****
// *****************************
$.fn[pluginName] = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || !method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist in jQuery.' + pluginName );
}
};
// ***************************
// ***** Fin: Supervisor *****
// ***************************
})(jQuery);
And here's a Demo.
Edit
Here's an attempt at explaining the fisheye (barrel distortion) calculations ...
Starting with a blank lens of w x h pixels.
The code loops through all pixels (target pixels).
For each target pixel, chooses a pixel (source pixel) from the background image.
The source pixel is always selected from those on (or close to) the same radial ray as the target, but at a smaller radial distance (using a formula for barrel distortion) from the lens's center.
This is mechanised by calculation of the polar coordinates (nr, theta) of the source pixel, then the application a standard math formula for converting polar back to rectangular coordinates nxn = nr * Math.cos(theta) and nxn = nr * Math.sin(theta). Up to this point, all calculations have been made in normalised -1...0...1 space.
The rest of the code in the block denormaises (rescales), and (the bit I had to heavily adapt) actually implements the source to target pixel mapping by indexing into the 1-dimensional source and target data.