Introduzione

In questo articolo vedremo come simulare una fonte di luce emessa dal cursore senza l’utilizzo di HTML5 o CSS3, in modo da ottenere un effetto compatibile anche con i browser più vecchi. Quel che serve è solo JavaScript e un paio di minuti liberi. Il risultato finale sarà il seguente:

tasto per vedere una demo

Logica di funzionamento

Vi anticipo che l’effetto è puramente ottico, si tratta di 3 immagini che fanno da sfondo a 3 div differenti:

1. Il logo

2. L’ombra del logo

3. Una sfera luminosa (PNG a sfondo trasparente)

Il punto chiave della simulazione è il cursore, infatti al muoversi del cursore 3 sono le cose che devono succedere:
1. Il logo deve restare fermo
2. La sfera luminosa deve seguire il cursore
3. L’ombra deve spostarsi in base alla posizione del cursore (ovvero la fonte di luce)

Questi eventi, a parte il primo, saranno gestiti interamente via javascript.

Per posizionare la sfera luminosa bisogna legarci all’evento $(document).mousemove():

var centroluceX		=	parseInt($("#luce").width()/2);
var centroluceY		=	parseInt($("#luce").height()/2);
$(document).mousemove(function(e){
	x = e.pageX; // salvo coordinata x del cursore
	y = e.pageY; // coordinata y
	$("#luce").css({ "left": x-centroluceX, "top": y-centroluceY});
});

Così facendo ad ogni spostamento del mouse possiamo sapere le sue coordinate x,y e associarle alla nostra immagine tenendo conto delle sue dimensioni in modo da centrarla correttamente.

Per ottenere invece un effetto di spostamento realistico dell’ombra è richiesto un minimo sforzo trigonometrico (Tutti i processi elencati andranno eseguiti ad ogni spostamento del cursore)

1. Calcoliamo il centro assoluto del logo (verrà considerato come un punto definito nello spazio offerto dal browser)

var centrologoX		=	parseInt($("#logo").width()/2);
var centrologoY		=	parseInt($("#logo").height()/2);
var logoX		=	parseInt($("#logo").position().left) + centrologoX;
var logoY		=	parseInt($("#logo").position().top) + centrologoY;

2. Calcoliamo la distanza tra le coordinate puntiformi x,y del cursore e quelle del logo

var dX	=	logoX - x; // distanza orizzontale punto-punto tra il cursore e il centro del logo
var dY	=	logoY - y; // distanza verticale

A questo punto abbiamo 2 segmenti dX e dY che rappresentano rispettivamente la distanza orizzontale e quella verticale che separa il centro del logo dalla fonte di luce (il cursore), proprio come se fossero i 2 cateti di un triangolo rettangolo:

var distance = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2)); // ipotenusa

3. Calcoliamo la nuova posizione dell’ombra

La posizione orizzontale sarà il coseno dell’angolo acuto moltiplicato per un fattore di offset (a piacere) in modo da descrivere un cerchio più ampio mentre quella verticale sarà il seno

offset = distance * 1.10;
shadowPosLeft	=	(dX / distance * offset ) + "px";
shadowPosTop	=	(dY / distance * offset ) + "px";

A questi valori andranno poi aggiunti i delta (x – centrologoX) nel caso del posizionamento orizzontale e (y – centrologoY) per quello verticale al fine di riposizionare l’ombra dietro al logo.

shadowPosLeft	=	(dX / distance * offset ) + x - centrologoX + "px";
shadowPosTop	=	(dY / distance * offset ) + y - centrologoY + "px";

Ed infine…

4. Effettuiamo il riposizionamento

$("#ombra").css({ "left": shadowPosLeft, "top": shadowPosTop, "opacity": 1.0 - offset/1000});
$("#luce").css({ "left": x-centroluceX, "top": y-centroluceY});

Spazio per le idee

Nel codice che trovate qui sotto ho pensato di aggiungere anche un interruttore (al click del mouse potete “spegnere” e “accendere” la luce) e di gestire l’evento di ridimensionamento della finestra.
Ma ovviamente si presta a qualsiasi idea di implementazione. Qualche spunto, a scapito di perderne in compatibilità, lo offre CSS3. Ad esempio le proprietà rotate, skewX e radial-gradient se implementate in modo appropriato permettono di date un tocco di realtà in più all’effetto di luce:

Sotto il codice, e sotto il codice i commenti 🙂

HTML e CSS

index.html

<html>
	<head>
		<title>gleenk.com | Dynamic Light Simulation (Marco Costantino)</title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<link rel="stylesheet" href="style.css" type="text/css" media="all">
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
		<script type="text/javascript" src="script.js"></script>
	</link></meta></head>
	<body>
		<div id="luce"></div>
		<div id="logo"></div>
		<div id="ombra"></div>
	</body>
</html>

style.css

/* gleenk.com | Dynamic Light Simulation (Marco Costantino) */
body { 
	background:#444; 
	margin:0; 
	padding:0; 
}
#luce { 
	position:absolute; 
	z-index:150;
	width:87px;
	height:83px;
	background:url(images/luce.png) no-repeat;
}
#logo, #ombra	{ 
	position:absolute; 
	width:526px; 
	height:135px; 
	top:25%; 
	left: 30%; 
	background:url(images/gleenk-logo.png) no-repeat 0 0; 
	z-index:100; 
}
#ombra { background:url(images/gleenk-logo-ombra.png) no-repeat 0 0; z-index:50;}

Javascript

script.js

/* gleenk.com | Dynamic Light Simulation (Marco Costantino) */
$(document).ready(function(){
	var x = 0; // coordinata X cursore
	var y = 0; // coordinata Y
	var interruttore = 1; // interruttore luce
	var centrologoX		=	parseInt($("#logo").width()/2);
	var centrologoY		=	parseInt($("#logo").height()/2);
	var centroluceX		=	parseInt($("#luce").width()/2);
	var centroluceY		=	parseInt($("#luce").height()/2);
	var offset = 0;
	riposizionaOmbra();
	function riposizionaOmbra(){
		logoX			=	parseInt($("#logo").position().left) + centrologoX;
		logoY			=	parseInt($("#logo").position().top) + centrologoY;
		dX				=	logoX - x; // distanza orizzontale punto-punto tra il cursore e il centro del logo
		dY				=	logoY - y; // distanza verticale
		distance		=	Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2)); // ipotenusa
		// La posizione orizzontale sarà il coseno dell'angolo acuto moltiplicato per un fattore di offset
		// le costanti x - centrologoX e y - centrologoY servono a posizionare l'ombra dietro al logo
		offset = distance * 1.10;
		shadowPosLeft	=	(dX / distance * offset ) + x - centrologoX + "px";
		// Per la posizione verticale utilizziamo il seno
		shadowPosTop	=	(dY / distance * offset ) + y - centrologoY + "px";
		//effettua il riposizionamento e imposto la trasparenza (che sarà proporzionale alla distanza del cursore)
		$("#ombra").css({ "left": shadowPosLeft, "top": shadowPosTop, "opacity": 1.0 - offset/1000});
		$("#luce").css({ "left": x-centroluceX, "top": y-centroluceY});
	}
	$(document).mousemove(function(e){
		x = e.pageX; // salvo coordinata x del cursore
		y = e.pageY; // coordinata y
		if(interruttore>0) // se la luce è accesa riposiziono l'ombra
			riposizionaOmbra();
	}); 
	$(document).click(function(){
		interruttore *= -1; // >0 = luce accesa | 0)
			riposizionaOmbra();
	});
});