Following the documentation it was easy to figure it out how to click on a mesh, but preventing the camera from going though a mesh not that easy. I need some guidelines.
How can I stop the camera from moving through messes using Raycaster?
jsbin
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - interactive cubes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body style="margin:0;overflow:hidden;">
<div style="position:fixed;background:rgba(255,255,255,0.9);" onmouseout="new function(){controls=new function(){this.moveX=0;this.moveY=0;this.moveZ=0;this.rotateX=0;this.rotateY=0;};}">Move RightMove LeftMove DownMove UpMove BackMove FrontRotate RightRotate LeftRotate UpRotate Down</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
<script>
var container;
var camera, scene, raycaster, renderer;
var mouse = new THREE.Vector2(),INTERSECTED=[],clickedIn/*bc starts like it was clicked*/=false,controls;
var clock = new THREE.Clock();
init();
animate();
function init() {
controls = new function () {
this.moveX = 0;
this.moveY = 0;
this.moveZ = 0;
this.rotateX = 0;
this.rotateY = 0;
}
container = document.createElement('div');
document.body.appendChild(container);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);
var geometry = new THREE.BoxBufferGeometry(20, 20, 20);
for (var i = 0; i < 50; i++) {
var object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff }));
object.name = 'Index:' + i;
object.userData.foo = 'foo';
object.position.x = Math.floor(Math.random() * 201) - 100;
object.position.y = Math.floor(Math.random() * 201) - 100;
object.position.z = Math.floor(Math.random() * 201) - 100;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add(object);
}
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xf0f0f0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
container.addEventListener('click', function (event) {
event.preventDefault();
clickedIn = true;
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
}, false);
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}, false);
}
function animate() {
requestAnimationFrame(animate);
// raycaster
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(scene.children);
for (var i = 0; i < INTERSECTED.length; i++) {
INTERSECTED[i].material.emissive.setHex(INTERSECTED[i].currentHex);
INTERSECTED.splice(i, 1);
}
for (var i = 0; clickedIn && i < intersects.length; i++) {
var length = INTERSECTED.push(intersects[0].object) - 1;
INTERSECTED.currentHex = INTERSECTED[length].material.emissive.getHex();
INTERSECTED[length].material.emissive.setHex(0xff0000);
}
// move
var delta = clock.getDelta(), step = 100, stepAngle = (Math.PI / 2);
if (controls.moveX == 1) camera.translateX(step * delta);
else if (controls.moveX == -1) camera.translateX(-step * delta);
if (controls.moveY == 1) camera.translateY(step * delta);
else if (controls.moveY == -1) camera.translateY(-step * delta);
if (controls.moveZ == 1) camera.translateZ(step * delta);
else if (controls.moveZ == -1) camera.translateZ(-step * delta);
if (controls.rotateX == 1) camera.rotateOnAxis(new THREE.Vector3(1, 0, 0), stepAngle * delta);
if (controls.rotateX == -1) camera.rotateOnAxis(new THREE.Vector3(1, 0, 0), -stepAngle * delta);
if (controls.rotateY == 1) camera.rotateOnAxis(new THREE.Vector3(0, 1, 0), stepAngle * delta);
if (controls.rotateY == -1) camera.rotateOnAxis(new THREE.Vector3(0, 1, 0), -stepAngle * delta);
camera.updateMatrixWorld();
// render
renderer.render(scene, camera);
}
</script>
</body>
</html>
For a First-Person Camera, I think that the right way to do that would be to use the bounding sphere of the camera and test it over each mesh of the scene, but if you really want to use a raycaster then I can think of 2 approaches :
Approach 1
In your rendering loop :
Update the position of your camera ;
For each object in your scene :
Create a ray that goes from the camera to the mesh and starts slightly before the camera;
Cast the ray. If an intersection is found and lies before the camera (1), move the camera to the intersection point.
Approach 2
In your rendering loop again :
Update the position of your camera ;
Create one ray that points towards the direction of the camera and starts slightly before the camera ;
Create another ray that points in the opposite direction of the camera and starts slightly after the camera ;
Cast the first ray. If an intersection is found and the intersection point lies before the camera (1) than move the camera to the intersection point ;
If no intersection is found, cast the second ray. If an intersection is found and the intersection point lies after the camera (2) than move the camera to the intersection point.
The first algorithm is in O(n), n being the number of objects in your scene whereas the second one is in O(1) but can be tricky with big meshes.
Related
I'm trying to make a FPS camera in WebGL.
When I move the camera from the initial position and then rotate, the camera moves along the start position instead of the new position.
The translation and rotation are made with glMatrix.js library.
I have the variables:
var rotateY = 0; // For rotating along the Y axis
var fronBackMovement = 0; // Front and back movement
var sideMovement = 0; // Movement from side to side
Keyboard events:
// Movement
if( key.w == true ){
fronBackMovement += 5;
}
if( key.a == true ){
sideMovement += 5;
}
if( key.s == true ){
fronBackMovement -= 5;
}
if( key.d == true ){
sideMovement -= 5;
}
Mouse movement:
// Rotation
if( mouse.movementX != 0 ){
rotateY += mouse.movementX; // mouse.movementX is screen movement of the cursor
}
Translation and rotation:
camera.matrix = mat4.fromTranslation( mat4.create() ,vec3.fromValues(sideMovement,0,fronBackMovement) )
camera.matrix = mat4.rotateY(camera.matrix,camera.matrix,rotateY )
The camera generally looks down the -Z axis so to move forward you just add the camera's Z axis to your position. If you don't want to move vertically then zero out the Y component and normalize. Finally multiply by your desired speed.
The camera's x-axis contains the side movement direction so you can do the same for that.
const cameraPosition = vec3.create();
const tempForwardDirection = vec3.create();
const tempSideDirection = vec3.create();
...
tempForwardDirection[0] = camera.matrix[8];
tempForwardDirection[1] = 0; // camera.matrix[9];
tempForwardDirection[2] = camera.matrix[10];
vec3.normalize(tempForwardDirection, tempForwardDirection)
tempSideDirection[0] = camera.matrix[0];
tempSideDirection[1] = camera.matrix[1];
tempSideDirection[2] = camera.matrix[2];
vec3.scaleAndAdd(
cameraPosition,
cameraPosition,
tempForwardDirection,
-fronBackMovement);
vec3.scaleAndAdd(
cameraPosition,
cameraPosition,
tempSideDirection,
sideMovement)
camera.matrix = mat4.fromTranslation(camera.matrix, cameraPosition);
camera.matrix = mat4.rotateY(camera.matrix,camera.matrix,rotateY);
let rotateY = 0; // For rotating along the Y axis
let fronBackMovement = 0; // Front and back movement
let sideMovement = 0; // Movement from side to side
const cameraPosition = vec3.create();
const tempForwardDirection = vec3.create();
const tempSideDirection = vec3.create();
const camera = {
matrix: mat4.create(),
};
const mouse = {
movementX: 0,
};
const gl = document.querySelector("canvas").getContext("webgl");
const vs = `
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
attribute vec4 position;
attribute vec3 normal;
varying vec3 v_normal;
void main() {
gl_Position = u_worldViewProjection * position;
v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
}
`;
const fs = `
precision mediump float;
varying vec3 v_normal;
uniform vec3 u_lightDir;
uniform vec4 u_color;
void main() {
vec3 norm = normalize(v_normal);
float light = dot(u_lightDir, norm) * .5 + .5;
gl_FragColor = vec4(u_color.rgb * light, u_color.a);
}
`;
const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
const projection = mat4.create();
const view = mat4.create();
const viewProjection = mat4.create();
const world = mat4.create();
const worldViewProjection = mat4.create();
const worldInverse = mat4.create();
const worldInverseTranspose = mat4.create();
const fov = degToRad(90);
const zNear = 0.1;
const zFar = 100;
const lightDir = vec3.normalize(vec3.create(), [1, 2, 3]);
const key = {};
let px = 0;
let py = 0;
let pz = 0;
let elev = 0;
let ang = 0;
let roll = 0;
const speed = 1;
const turnSpeed = 90;
let then = 0;
function render(now) {
now *= 0.001; // seconds;
const deltaTime = now - then;
then = now;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.useProgram(progInfo.program);
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
mat4.perspective(projection, fov, aspect, zNear, zFar);
fronBackMovement = 0;
sideMovement = 0;
if( key.w == true ){
fronBackMovement = 5 * deltaTime;
}
if( key.a == true ){
sideMovement = -5 * deltaTime;
}
if( key.s == true ){
fronBackMovement = -5 * deltaTime;
}
if( key.d == true ){
sideMovement = 5 * deltaTime;
}
// Rotation
if( mouse.movementX != 0 ){
rotateY += mouse.movementX / 200; // mouse.movementX is screen movement of the cursor
mouse.movementX = 0;
}
tempForwardDirection[0] = camera.matrix[8];
tempForwardDirection[1] = 0; // camera.matrix[9];
tempForwardDirection[2] = camera.matrix[10];
vec3.normalize(tempForwardDirection, tempForwardDirection)
tempSideDirection[0] = camera.matrix[0];
tempSideDirection[1] = camera.matrix[1];
tempSideDirection[2] = camera.matrix[2];
vec3.scaleAndAdd(
cameraPosition,
cameraPosition,
tempForwardDirection,
-fronBackMovement);
vec3.scaleAndAdd(
cameraPosition,
cameraPosition,
tempSideDirection,
sideMovement)
camera.matrix = mat4.fromTranslation(camera.matrix, cameraPosition);
camera.matrix = mat4.rotateY(camera.matrix,camera.matrix,rotateY);
mat4.invert(view, camera.matrix);
mat4.multiply(viewProjection, projection, view);
for (let z = -1; z <= 1; ++z) {
for (let y = -1; y <= 1; ++y) {
for (let x = -1; x <= 1; ++x) {
if (x === 0 && y === 0 && z === 0) {
continue;
}
mat4.identity(world);
mat4.translate(world, world, [x * 3, y * 3, z * 3]);
mat4.multiply(worldViewProjection, viewProjection, world);
mat4.invert(worldInverse, world);
mat4.transpose(worldInverseTranspose, worldInverse);
twgl.setBuffersAndAttributes(gl, progInfo, bufferInfo);
twgl.setUniforms(progInfo, {
u_worldViewProjection: worldViewProjection,
u_worldInverseTranspose: worldInverseTranspose,
u_color: [(x + 2) / 3, (y + 2) / 3, (z + 2) / 3, 1],
u_lightDir: lightDir,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
}
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
window.addEventListener('keydown', (e) => {
key[e.key] = true;
e.preventDefault();
});
window.addEventListener('keyup', (e) => {
key[e.key] = false;
e.preventDefault();
});
window.addEventListener('mousemove', (e) => {
mouse.movementX = e.movementX;
});
function degToRad(d) {
return d * Math.PI / 180;
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
pre { position: absolute; left: 1em; top: 0; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
<canvas></canvas>
<pre>
A = left
D = right
W = forward
S = down
</pre>
I created a chart using html canvas. The result would like it to be printed as a PDF file using Kendo. It works, but the graphic quality is very poor. For the solution I need I can't use kendo chart for limitation reasons
report.html
<div class="width-100-perc text-center">
<canvas id="canvas" width="100" height="100"></canvas>
<br />
</div>
report.ts
drawChart() {
console.log( 'foi');
const canvas: HTMLCanvasElement = (<HTMLCanvasElement>document.getElementById('canvas'));
console.log(this.series);
if (canvas) {
const ctx = canvas.getContext('2d');
// Base offset distance of 10
const offset = 0;
let beginAngle = 0;
let endAngle = 0;
// Used to calculate the X and Y offset
let offsetX, offsetY, medianAngle;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fill();
for (let i = 0; i < this.angles.length; i = i + 1) {
beginAngle = endAngle;
endAngle = endAngle + this.angles[i];
// The medium angle is the average of two consecutive angles
medianAngle = (endAngle + beginAngle) / 2;
// X and Y calculations
offsetX = Math.cos(medianAngle) * offset;
offsetY = Math.sin(medianAngle) * offset;
ctx.beginPath();
ctx.fillStyle = this.series[0].data[i].color;
// Adding the offsetX and offsetY to the center of the arc
ctx.moveTo(50 + offsetX, 50 + offsetY);
ctx.arc(50 + offsetX, 50 + offsetY, 40, beginAngle, endAngle);
ctx.lineTo(50 + offsetX, 50 + offsetY);
ctx.fill();
}
if (this.angles.length > 0) {
ctx.beginPath();
ctx.fillStyle = '#FFFFFF';
ctx.arc(50, 50, 15, 0, 2 * Math.PI);
ctx.fill();
}
}
}
This is not a problem with kendo's pdf export. Rather, it's inherent to the way the HTML canvas works. Your export looks distorted and pixelated because, at the end of the day, it's just a 100x100 image, which is rather low resolution. I'm assuming you want it to be that small since it is made to fit a specific part of the page. If you just directly export this canvas, that pixelated image is what you should expect.
I can propose this workaround. You need to refactor your drawChart() method to take into account a scale (number). This would mean multiplying all x,y coordinates and dimensions by this value. By default, the scale is 1. When exporting to pdf, you will follow these steps:
Change the scale to higher value, let's say 10
Draw
Export to pdf
Change scale to 1 again
Draw
This way, the chart is temporarily redrawn using a higher resolution canvas. In it's high(er) resolution state, it's exported and then it's redrawn with its original dimensions.
If you provide some example values of your this.angles and this.series I can refactor your drawChart() function to take this into account. As it stands, I can't. But I've prepared a similar example here. This is the ReportComponent I've created.
report.component.html
<button (click)="savePdf(false)">bad pdf</button>
<button (click)="savePdf(true)">good pdf</button>
<br/>
<kendo-pdf-export #pdf>
<canvas #canvas [width]="baseWidth" [height]="baseHeight"></canvas>
</kendo-pdf-export>
report.component.ts
export class ReportComponent implements AfterViewInit {
#ViewChild("canvas", { static: false })
public canvasRef: ElementRef<HTMLCanvasElement>;
#ViewChild("pdf", { static: false })
public pdf: PDFExportComponent;
#Input() public title: string = "";
public scale: number = 1;
public baseWidth: number = 100;
public baseHeight: number = 100;
constructor() {}
ngAfterViewInit() {
this.draw();
}
draw() {
const canvas = this.canvasRef.nativeElement;
canvas.width = this.baseWidth * this.scale; // scaled
canvas.height = this.baseHeight * this.scale; // scaled
const context = canvas.getContext("2d");
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 31.4 * this.scale; // scaled
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "green";
context.fill();
context.lineWidth = 5 * this.scale; // scaled
context.strokeStyle = "#003300";
context.stroke();
}
savePdf(good: boolean) {
if (good) {
// scale 10x and re-draw
this.scale = 10;
this.draw();
this.pdf.saveAs("good.pdf");
this.scale = 1;
this.draw();
} else {
// just draw as is
this.pdf.saveAs("bad.pdf");
}
}
}
Bad PDF
Good PDF
I have a simple scene in WebGL where i store every transformation (for the camera and the models) in a single model/view matrix and i set them by rotating and moving said matrix.
What i want is, to being able to rotate the camera around and when i "move forward" to move towards where the camera is pointing.
So far, i have modified this code to this:
mat4.identity(mvMatrix);
mat4.rotateX(mvMatrix, degToRad(elev), mvMatrix);
mat4.rotateY(mvMatrix, degToRad(ang), mvMatrix);
mat4.rotateZ(mvMatrix, degToRad(-roll), mvMatrix);
mat4.translate(mvMatrix, [-px, -py, -pz], mvMatrix);
since it wasn't working as it was and it kind of works, until you do an extreme rotation (more than 90 degrees).
This is not a deal breaker for what i'm doing, but i want to know. Is this the best i can get without moving away from calculating the camera orientation like this?
WebGL cameras generally point down the -Z axis so to move in the direction the camera is facing you just add the camera's Z axis (elements 8, 9, 10) to the position of the camera multiplied by some velocity.
const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector("canvas").getContext("webgl");
const vs = `
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
attribute vec4 position;
attribute vec3 normal;
varying vec3 v_normal;
void main() {
gl_Position = u_worldViewProjection * position;
v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
}
`;
const fs = `
precision mediump float;
varying vec3 v_normal;
uniform vec3 u_lightDir;
uniform vec4 u_color;
void main() {
vec3 norm = normalize(v_normal);
float light = dot(u_lightDir, norm) * .5 + .5;
gl_FragColor = vec4(u_color.rgb * light, u_color.a);
}
`;
const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
const projection = m4.identity();
const camera = m4.identity();
const view = m4.identity();
const viewProjection = m4.identity();
const world = m4.identity();
const worldViewProjection = m4.identity();
const worldInverse = m4.identity();
const worldInverseTranspose = m4.identity();
const fov = degToRad(90);
const zNear = 0.1;
const zFar = 100;
const lightDir = v3.normalize([1, 2, 3]);
const keys = {};
let px = 0;
let py = 0;
let pz = 0;
let elev = 0;
let ang = 0;
let roll = 0;
const speed = 1;
const turnSpeed = 90;
let then = 0;
function render(now) {
now *= 0.001; // seconds;
const deltaTime = now - then;
then = now;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.useProgram(progInfo.program);
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
m4.perspective(fov, aspect, zNear, zFar, projection);
m4.identity(camera);
m4.translate(camera, [px, py, pz], camera);
m4.rotateX(camera, degToRad(elev), camera);
m4.rotateY(camera, degToRad(-ang), camera);
m4.rotateZ(camera, degToRad(roll), camera);
m4.inverse(camera, view);
m4.multiply(projection, view, viewProjection);
for (let z = -1; z <= 1; ++z) {
for (let y = -1; y <= 1; ++y) {
for (let x = -1; x <= 1; ++x) {
if (x === 0 && y === 0 && z === 0) {
continue;
}
m4.identity(world);
m4.translate(world, [x * 3, y * 3, z * 3], world);
m4.multiply(viewProjection, world, worldViewProjection);
m4.inverse(world, worldInverse);
m4.transpose(worldInverse, worldInverseTranspose);
twgl.setBuffersAndAttributes(gl, progInfo, bufferInfo);
twgl.setUniforms(progInfo, {
u_worldViewProjection: worldViewProjection,
u_worldInverseTranspose: worldInverseTranspose,
u_color: [(x + 2) / 3, (y + 2) / 3, (z + 2) / 3, 1],
u_lightDir: lightDir,
});
twgl.drawBufferInfo(gl, bufferInfo);
}
}
}
if (keys['87'] || keys['83']) {
const direction = keys['87'] ? 1 : -1;
px -= camera[ 8] * deltaTime * speed * direction;
py -= camera[ 9] * deltaTime * speed * direction;
pz -= camera[10] * deltaTime * speed * direction;
}
if (keys['65'] || keys['68']) {
const direction = keys['65'] ? 1 : -1;
ang += deltaTime * turnSpeed * direction;
}
if (keys['81'] || keys['69']) {
const direction = keys['81'] ? 1 : -1;
roll += deltaTime * turnSpeed * direction;
}
if (keys['38'] || keys['40']) {
const direction = keys['38'] ? 1 : -1;
elev += deltaTime * turnSpeed * direction;
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
window.addEventListener('keydown', (e) => {
keys[e.keyCode] = true;
e.preventDefault();
});
window.addEventListener('keyup', (e) => {
keys[e.keyCode] = false;
e.preventDefault();
});
function degToRad(d) {
return d * Math.PI / 180;
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
pre { position: absolute; left: 1em; top: 0; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
<pre>
A = left
D = right
W = forward
S = down
Q = roll left
E = roll right
UP = look up
DN = look down
</pre>
So I have a panning feature with a basic HTML input range I am using the following javascript code to control the panning:
var x = this.valueAsNumber,
y = 0,
z = 1 - Math.abs(x);
panner2.setPosition(x,y,z);
Any idea how to implement it with jQuery-Knob library? I have done it with volume control using change function and reading in value but panning uses more than one value so unsure how to implement it.
Example implementation. I did this using another formula for equal power panning and it isn't perfect but maybe it can help you understand how to affect variables with the knob plug in.
<div class = "container">
<div id="pad" style=""></div>
<input id="panDial" class="dial" data-min="0" data-max="360" value="0" data-width="100">
</div>
$(function() {
var audioContext = new AudioContext();
var oscillator;
var val;
var x = 0;
var y = 0;
var z = 0;
var xDeg = 0;
var zDeg = xDeg + 90;
var valuePanDial = document.getElementById("panDial").value;
$(".dial").knob({
change: function(valuePanDial) {
xDeg = +valuePanDial;
zDeg = 180 - zDeg;
x = Math.sin(xDeg * (Math.PI / 180));
z = Math.sin(zDeg * (Math.PI / 180));
}
});
$("#pad").on("mousedown", function() {
oscillator = audioContext.createOscillator();
var panner = audioContext.createPanner();
panner.setPosition(x, y, z);
panner.panningModel = 'equalpower';
oscillator.frequency.value = 100;
oscillator.type = "sawtooth";
oscillator.connect(panner); // Connects it to output
panner.connect(audioContext.destination);
oscillator.start(audioContext.currentTime);
})
$("#pad").on("mouseup", function() {
oscillator.stop(audioContext.currentTime);
})
});
.container{
position:relative;
top:30px;
width:700px;
left:40%;
}
#pad {
background-color:orange;
margin-bottom:20px;
opacity:0.8;
border-style:solid;
border-color:grey;
width:200px;
height:100px;
}
#freqDial{
width:10px;
height: 10px;
}
I'm looking for a thumbnail tiles scroller/slideshow/slider
Here is the example http://safari.to/clients
Really appreciate if anyone could help :) Thanks
When you see something on any website, its easy to inspect and see how they are doing it.
The website mentioned by you is doing it using their own script instead of a plug in
See the following code from http://safari.to/assets/js/script.js. You will also need to see how they are styling the sliders by inspecting their CSS code
// Agencies slide
var clients = Math.floor(Math.random() * 2) + 1; // nth-child indices start at 1
if ( clients == 1){
$('.agencies.clients').hide();
}
else
{
$('.brands.clients').hide();
}
$('.agencies menu a').bind({
click: function()
{
if(sliding) return false;
var pointer = $(this);
var width = $('.agencies .scroller li').length * 137;
var current = parseInt($('.agencies .scroller').css('left'));
var distance = 0;
if(pointer.is('.right-pointer'))
{
if(current == -1920) distance = current - 137;
else distance = current - 960;
if((width + current) < 960)
distance = current;
}
else
{
distance = current + 1097;
if(distance > 0)
distance = 0;
}
sliding = true;
$('.scroller').animate({
left: distance + 'px'
}, 300,
function()
{
sliding = false;
});
}
});