Mostrando las entradas con la etiqueta javascript. Mostrar todas las entradas
Mostrando las entradas con la etiqueta javascript. Mostrar todas las entradas

2019/09/29

Vue Simple App: Sin webpack

Vue, Router, Vuex y Vuetify, pero sin webpack

[Actualizado el 2019/11/05: "Carga dinámica"]

Tal vez no tenga ninguna razón técnica importante para preferir prescindir de webpack al desarrollar una aplicación web.

Tal vez sea solamente nostalgia por los viejos tiempos, donde simplemente importabas librerías en la página y la magia podía aparecer.

Tal vez no sea mala idea tener esa capa extra que cargar en el viaje, y aceptarla a cambio de los caramelos que nos regala.

Pero a mi me molesta un poco.

Preferiría no tener que usarla si puedo evitarlo.

Siento que hay que tener cuidado cuando algo aumenta la complejidad. Mayor velocidad o comodidad pueden ser justificaciones. Pero hay que estar atentos a no limitar la diversidad de opciones.

Una de las cosas que me gusta de Vue es que puedo empezar muy simple, importando la librería en la página. Y luego, si el problema lo amerita, ir escalando la solución hacia algo más y más complejo. Es como empezar descalzo si quiero, o con sandalias, e ir cambiando según lo amerite el terreno.

La mayoría de tutoriales parece estar de acuerdo en que usar vue create es la opción más práctica. Sin embargo, algo que descubrí es que también se puede llegar a elaborar soluciones relativamente complejas sin usar un transpilador.

Esta es una guía en un proceso de desarrollo simple que nos permita usar Vue, Router, Vuex y Vuetify, sin necesitar la complejidad extra de webpack.

HTML5

Un lugar simple donde empezar.

HTML5: https://codepen.io/akobashikawa/pen/NWKVazJ

HTML5 + Vue

Importar vue y desarrollar un script en la misma página.

HTML5 + Vue: https://codepen.io/akobashikawa/pen/aborLgW

HTML5 + Vue + Router

Importar Vue Router para organizar la navegación.

Eso también nos empuja a organizar el contenido en componentes, si es que no lo hemos hecho ya.
HTML5 + Vue + Router: https://codepen.io/akobashikawa/pen/qBWGVEp

HTML5 + Vue + Router + Vuex

Importar Vuex para compartir un estado general entre los componentes.
HTML5 + Vue + Router + Vuex: https://codepen.io/akobashikawa/pen/pozmdby

HTML5 + Vue + Router + Vuex + Vuetify

Para darle un look de app con Material Design.

HTML5 + Vue + Router + Vuex + Vuetify: https://codepen.io/akobashikawa/pen/bGbyYod

Módulos

Cuando más componentes van apareciendo y el largo del archivo va haciendo complicada la edición, parece una señal para sacar el código javascript a otro archivo.

Javascript soporta import. Pero hay que usarla dentro de un módulo.

Algo que ocurre con los módulos declarados normalmente, es que se carga toda la estructura de dependencias desde el inicio.

Para prevenirlo, y que cada módulo se pueda cargar cuando se necesita y no antes, se puede usar la técnica de declarar el componente como resultado de una función:

Antes:
import Counter from './Counter.js';
Después:
const Counter = () => import('./Counter.js');

Conclusión

Es posible desarrollar vue apps de relativa complejidad sin necesidad de usar webpack.

Me parece que este esquema puede simplificar el inicio de un proyecto. Aún cuando la versión para producción pueda requerir webpack para las optimizaciones, se podría usar esto durante el desarrollo.

¿Qué ventajas o desventajas ves hacerlo de este modo?, ¿te parece que puede facilitar la depuración?, ¿crees que en el futuro los navegadores carguen los scripts de un modo que no sea necesario empaquetarlos como actualmente?

2019/01/11

2017/02/17

Invertir una cadena con javascript

¿Cómo harías una función que invirtiera una cadena dada?
Es decir que f("abcdef") sea "fedcba".


Yo pienso que no hay una "forma correcta" de resolverlo. Simplemente hay formas, y cualquiera que se te ocurra y haga el trabajo está bien.

De todas esas formas habrá algunas que logran hacerlo con menos variables o menos código, o usando técnicas esotéricas. Formas optimizadas.

Hay programadores que se familiarizan con esas técnicas optimizadas y las aplican luego directamente en los siguiente problemas que enfrentan. Puede ser impresionante, como cuando ves a un mago sacando del sombrero cosas que no creías posible. Igual que con los magos, se requiere de práctica y ensayo para lograr esas cosas. Pero, igual que con los magos, no significa que esa es la forma en que todo el mundo debería pretender hacer las cosas.

La optimización prematura es la raíz del mal, es la advertencia de Knuth, comprobada una y otra vez en la experiencia de los programadores. Está bien optimizar, por supuesto, pero tiene su momento, y definitivamente no es al inicio.

En la vida real, la solución de los problemas se produce por tanteo. Como en un cuarto oscuro, estiras los brazos y tientas. Tocas algo una y otra vez, formándote una idea de lo que hay. Así, descubres la puerta hacia la solución. Una vez que la descubriste, podrías volver al punto de partida y hacerlo más rápido, en menos tiempo o de manera más elegante. Recién ahí es que se realiza la optimización. Si intentaras salir a toda velocidad al inicio, sería muy probable que te lastimaras con algún obstáculo o fueras rápido en la dirección equivocada. También sería un poco raro hacer movimientos elegantes si ni siquiera sabes a dónde vas a ir.

Así que para este problema, quizás se te podría ocurrir algo como:

// recorrer la cadena en reversa
function f(s) {
 var result = "";
 for (var i=s.length; i>0; i--) {
  result = result + s[i-1];
 }
 return result;
}

Ok, es lo primero que se me ocurrió. Veamos si se puede hacer mejor. Quizás se pueda recorrer la cadena de otro modo:

// recorrer la cadena de frente pero acumular en reversa
function g(s) {
 var result = "";
 for (var i=0; i<s.length; i++) {
  result = s[i] + result;
 }
 return result;
}

¿Y si vemos como luce con reduce?:

// usando reduce y acumulando en reversa
function h(s) {
 return s.split("").reduce(function(result, c) {
  return c + result;
 });
}

¿Y usando la notación fat arrow?:

// usando reduce y fat arrow
function j(s) {
 return s.split("").reduce((result, c) => c + result);
}

Quizás con la práctica sea esta forma la que aparezca primero en tu cabeza en el futuro. Podría ser impresionante. Pero hay que recordar que las cosas empiezan tanteando. Y que está bien.

2015/12/23

Unobtrusive Template Engine para Express


El uso de los sistemas de templates típicos (como jade, handlebars, jinja, etc), involucra aprender el lenguaje del sistema de templates y procesar el html base para que se acomode a los requerimientos del sistema de template.

El proceso se llama templating. El templating para jade es diferente que el de handlebars, o jinja.

Si uno usa express con jade, los templates que obtenga no servirán para express con handlebars.

Y esa forma de trabajo está bastante extendida. Si uno usa Drupal, los templates que obtenga no servirán para Wordpress. Aún más, si uno usa Drupal 6, los templates que obtenga no servirán para Drupal 7.

Todas las horas que se invierten en un templating no se pueden reutilizar si se cambia de framework. Es algo que mucha gente ve como algo natural pero me molesta desde hace algún tiempo :-P

Hace un tiempo, ensayé la idea con php y querypath. También con Wordpress y querypath.

http://akcideas.blogspot.pe/2011/05/unobstrusive-web-dev.html

Esta vez con nodejs-express con cheerio.

https://github.com/akobashikawa/express-unobtrusive-template-engine

La visión

La idea es hacer los reemplazos necesarios usando los mecanismos de jquery. Encontrar un modo en que el código base html no se toque.

Imagina que maquetas un site en html5. Puro frontend. Tomas ese conjunto de archivos y los dejas caer en cierto directorio de un framework. Y que el framework sea capaz de usarlo tal cual, sin que tengas que procesarlo. Luego, paulatinamente, unobtrusivamente, vas haciendo los reemplazos dinámicos.

Claro que hay que hace templating de todos modos, pero ya no es en el html, sino en el backend de la vista.

Me parece que sería más productivo desarrollar de ese modo.

2015/09/09

Node: de 0.12.5 a 4

NodeJS se ha actualizado a la versión 4.


2012/09/24

Resolviendo Soporte Drag para Flexslider

Es posible usar el plugin jquery.event.drag para proveer soporte para drag a Flexslider (es decir que permita arrastrar los sliders con el ratón en el desktop, de modo similar a como permite hacerlo en un dispositivo tipo touch):

- Usando el plugin jquery.event.drag (http://threedubmedia.com/code/event/drag/)
- En jquery.flexslider.js, copio el bloque que da soporte a mousewheel y lo uso como base para el soporte para drag:
...
// MOUSEWHEEL:
if (vars.mousewheel) {
  slider.bind('mousewheel', function(event, delta, deltaX, deltaY) {
    event.preventDefault();
    var target = (delta < 0) ? slider.getTarget('next') : slider.getTarget('prev');
    slider.flexAnimate(target, vars.pauseOnAction);
  });
}
// DRAG:
if (vars.drag) {
  slider.bind('drag', function(event, delta) {
    event.preventDefault();
    var target = (delta.deltaX < 0) ? slider.getTarget('next') : slider.getTarget('prev');
    slider.flexAnimate(target, vars.pauseOnAction);
  });
}
...

- También agrego 'drag' en los defaults de flexslider:
...
$.flexslider.defaults = {
  ...
  mousewheel: false,
  drag: false,
...

- Entonces, en el script, se puede usar 'drag: true' cuando se requiera:

$('.flexslider').flexslider({
  animation: 'slide',
  slideshow: false,
  drag: true
});

Referencia:  Request: add mouse dragging for desktop users

2012/05/25

Vertical Site

Introducción
La motivación fue hacer un site similar a:
Revisando believein (pude bajar el site usando wget, Firebug y un poco de paciencia), me resultaba un poco difícil encontrar la clave del comportamiento que veía.

Encontré una aproximación en Curtain.js. Sin embargo, cuando requería modificar la presentación, también me resultaba un poco difícil encontrar dónde hacerla.

Decidí reinventar la rueda de este caso, para tratar de comprender las ideas del proceso de solución.

Los sites que revisé me dieron pistas.

En este tutorial trato de resumir el proceso.

vertical-site@github

Simple HTML
  • Todo el contenido está presente en el mismo documento.
  • El contenido se divide en secciones a las que denomino pages.
  • Cada page es el destino de un link del menú. Esto permite una navegación simple a enlaces internos.
Aplicando estilos básicos:

index.html
style.css

Nav fixed
  • Se fijan los elementos de navegación: cabecera y menú.
    En simple_html, estaban dentro de page-0 (para permitir ver el menú cuando se fuera a HOME). Como ahora serán siempre visibles, eso no es necesario.
  • Se implementa un indicador de posición, que señale en el menú la página activa.
    • En el menú, aparece resaltado el link a.active
    • El link se activa si el url cambia
    • El url cambia
      • cuando se ingresa a mano
      • cuando se hace click en un enlace del menú
      • cuando se hace scroll
    • El plugin address permite responder ante un cambio del url.
index.html
style.css
script.js

Scrollto
  • Scroll animado suave.

index.html
style.css
script.js

(continuará...)

2011/10/28

Construyendo un Wysiwyg Editor con jQuery

En HTML, normalmente un textarea no es wysiwyg, pero existen plugins javascript, como CKEditor, NicEdit, entre otros, que ayudan a que lo sea. Se instala el plugin, se configura y listo. ¿Te animarías a hacerlo tú mismo?


WYSIWYG
Whay You See Is What You Get: lo que ves es lo que obtienes
Así es como se denomina a los editores que muestran la apariencia final del documento mientras es editado.

Buscando hacerme una herramienta para tomar notas con más facilidad, entré a explorar en este tema.

Antes, estaba envuelto por un halo misterioso. Suponía que sería una cuestión demasiado complicada. Descubrí que es más fácil de lo que parece.

Antes, para hacer editable un div, lo que se me ocurría era montar encima un elemento editable, como un input text, que se muestre cuando se necesite la edición. Pero, limitándome a eso, no veía cómo era posible mostrar en negrita o cursiva algo dentro de un elemento editable. Imaginaba que tendría que ver con algún tipo de magia desconocida que involucraría código muy complejo y sofisticado.

Resulta que es posible hacer que un div sea editable (¿por qué no difunden más esas cosas?).

Cuando un div es editable, está disponible un API que permite ejecutar comandos sobre su contenido (!).

De ese modo, hacer un editor wysiwyg se convierte en una cuestión muy, muy accesible, incluso para un programador como yo :-)

designMode
Al parecer, IE fue el que primero implementó la idea de que un elemento fuera editable, luego los otros navegadores acogieron la idea y ahora ya es algo estándar.

Esto se consigue haciendo que designMode="On" para el objeto que se desea editar.

Cuando se está en designMode="On", se pueden enviar comandos de formato usando la función execCommand. Estos permanecen activos hasta que se vuelven a enviar otra vez. O se aplican sobre el texto que estuviera seleccionado. Sí, como en Word. Hasta funciona deshacer con CTRL+Z

La idea básica
Una de las primeras guías que encontré usa un iframe y botones.

Pone el iframe en designMode="On". Hecho esto, se puede editar el contenido del iframe como si se estuviera en un textarea.

Los botones permiten invocar los comandos de formato.

Este es un ejemplo basado en: http://jsfiddle.net/Kxmaf/6/ :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>WYSIWYG Demo</title>

<script type="text/javascript" src="js/jquery-1.6.4.min.js"></script>

<style type="text/css">
input[type=button] {
  border: 1px solid #ccc;
}
#editor {
  width: 500px;
  height: 170px;
}
#bold {
  font-weight: bold;
}
#italic {
  font-style: italic;
}
</style>

<script type="text/javascript">
$(document).ready(function() {
  var cw = document.getElementById('editor').contentWindow;
  var doc = cw.document;
  doc.designMode = "on";
  doc.write('');
  doc.close();
  
  $('#bold').click(function() {
    cw.focus();// IE
    doc.execCommand('bold', false, false);
    cw.focus();
  });
  $('#italic').click(function() {
    cw.focus();// IE
    doc.execCommand('italic', false, false);
    cw.focus();
  });
  $('#fontname').change(function() {
    cw.focus();// IE
    doc.execCommand('fontname', false, $(this).val());
    cw.focus();
  });
});
</script>

</head>
<body>
  <h1>WYSIWYG Demo</h1>
  
  <div id="buttons">
    <input type="button" id="bold" value="B" />
    <input type="button" id="italic" value="I" />
    <select id="fontname">
      <option value="Arial">Arial</option>
      <option value="Comic Sans MS">Comic Sans MS</option>
      <option value="Courier New">Courier New</option>
      <option value="Monotype Corsiva">Monotype</option>
      <option value="Tahoma">Tahoma</option>
      <option value="Times">Times</option>
    </select>
  </div>
  <iframe id="editor"></iframe>
  
</body>
</html>

Mejorando
Es posible usar un div en lugar de un iframe. También se puede aprovechar más cosas de jQuery.

Este es un ejemplo basado en de77: jQuery Simple WYSIWYG Editor :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>WYSIWYG Demo</title>

<script type="text/javascript" src="js/jquery-1.6.4.min.js"></script>

<style type="text/css">
input[type=button] {
  border: 1px solid #ccc;
}
#editor {
  width: 500px;
  height: 170px;
  border: 1px solid #ccc;
}
#bold {
  font-weight: bold;
}
#italic {
  font-style: italic;
}
</style>

<script type="text/javascript">

$(document).ready(function() {
  function action(a,p) {
    $('#editor').focus();
    if (p == null) p = false;
    document.execCommand(a, null, p);
  }

  $('#editor').get(0).contentEditable = "true";
  
  $('#bold').click(function() {
    action('bold');
  });
  $('#italic').click(function() {
    action('italic');
  });
  $('#fontname').change(function() {
    action('fontname', $(this).val());
  });
  $('#html').click(function() {
    alert($('#editor').html());
  });
});
</script>

</head>
<body>
  <h1>WYSIWYG Demo</h1>
  
  <div id="buttons">
    <input type="button" id="bold" value="B" />
    <input type="button" id="italic" value="I" />
    <select id="fontname">
      <option value="Arial">Arial</option>
      <option value="Comic Sans MS">Comic Sans MS</option>
      <option value="Courier New">Courier New</option>
      <option value="Monotype Corsiva">Monotype</option>
      <option value="Tahoma">Tahoma</option>
      <option value="Times">Times</option>
    </select>
    <input type="button" id="html" value="HTML" />
  </div>
  <div id="editor"></div>
  
</body>
</html>

Me parece que esto puede ayudar a aclarar el tema para empezar. Ojalá le sirva de ayuda.

Referencias

2010/11/30

Cómo obtener las partes de una ruta usando javascript


Una ruta como http://par1/par2/par3/par4/par5 se puede separar en php usando algo como explode().
En javascript, una forma de hacerlo es usando split(). Por ejemplo:

var path = 'http://par1/par2/par3/par4/par5';
var parts = path.split('/');
var n = parts.length;
var par5 = parts[n-1];
var par4 = parts[n-2];
var par3 = parts[n-3]; // y así sucesivamente...

2010/08/27

Un mapa con Raphael

Raphäel es una biblioteca javascript que permite incorporar a la página imágenes vectoriales.

Píxeles y vectores

A diferencia de una fotografía formada por pixeles, una imagen vectorial está determinada por vectores. Mientras que hay que guardar la información de cada pixel, en el caso de los vectores sólo hay que guardar la información de sus extremos o puntos notables; los demás serán calculados a partir de ellos.

Eso permite que una imagen vectorial ocupe, en general, mucho menos espacio en disco que una imagen fotográfica. Además, se escala y siempre está nítida. No ocurre el 'pixeleo' que se suele notar en las fotos cuando se fuerza una ampliación.

Esas características hacen que sean una opción atractiva para gráficos web. Flash, por ejemplo, usa imágenes vectoriales.

SVG

SVG es un estándar XML para imágenes vectoriales en web. Raphael se apoya príncipalmente en ese estandar, de modo que cada elemento gráfico sea parte del DOM (el árbol XML que representa la página web), y provee adaptadores para compatibilidad entre navegadores (todavía no todos los navegadores soportan SVG del mismo modo).

El que un elemento gráfico sea parte del DOM permite manipularlo con javascript.

Usando Raphael

¿Qué tan fácil de usar es Raphael? Bueno, la entrada me pareció relativamente sencilla. Declarar la biblioteca raphael.js, un canvas y luego crear elementos gráficos como rectángulos, círculos, etc.
...
  <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
  <script type="text/javascript" src="js/raphael-min.js"></script>
  <script type="text/javascript">
    $(function() {
      var paper = Raphael('canvas', 800, 600);
      var circle = paper.circle(150, 150, 100);
      var circle2 = paper.circle(150, 450, 100);
      var ellipse = paper.ellipse(150, 150, 100, 50);
      var ellipse2 = paper.ellipse(150, 150, 50, 100);
      var ellipse3 = paper.ellipse(350, 350, 50, 100);
      var rect = paper.rect(0, 0, 800, 600);
      var rect2 = paper.rect(50, 50, 200, 200);
      var rect3 = paper.rect(270, 50, 200, 200, 20);
      var image = paper.image('images/bee.jpg', 490, 50, 100, 75);
      var set = paper.set();
      set.push(circle, rect3);
      set.attr({fill: "gold"});
      var text = paper.text(150, 280, 'Hello World!\nLos niños de Andalucía');
      var path = paper.path("M50 50L250 250");
      var path2 = paper.path("M250 50L50 250");
      //paper.clear();
      circle.paper.path("M10,10L50,50M50,10L10,50")
        .attr({stroke: "red"});
      ellipse2.remove();
      ellipse2.show(); // not show because previous remove
      ellipse.hide();
      ellipse.show();
      rect3.rotate(10);
      image.rotate(80, 490, 50);
      image.translate(100, 0);
      image.scale(1.1, 1.1);
      image.animate({'translation':'-100, 10'}, 1000, 'bounce');
      circle2.animate({
        "20%": {cx: 20, r: 20, easing: ">"},
        "50%": {cx: 70, r: 120, callback: function () {}},
        "100%": {cx: 10, r: 10}
      }, 2000);
      var c = paper.circle(200, 200, 50),
        r = paper.rect(200, 200, 50, 50);
      c.animate({cx: 20, r: 20}, 2000);
      r.animateWith(c, {x: 20}, 2000);
      var path3 = paper.path("M300,300c0,100 100-100 100,0c0,100 -100-100 -100,0z");
      var spot = paper.circle(300, 300, 4).attr({fill: 'red'});
      spot.animateAlong(path3, 4000);
    }
  </script>
  ...
  <div id="canvas"></div>
  ...
El paso siguiente, la interactividad y el manejo de eventos sí me pareció más complicado:
...
  circle.drag(
    function(dx, dy) {// move
      this.attr({cx: this.ox + dx, cy: this.oy + dy, opacity: .5});
    },
    function() {// start
      this.ox = this.attr("cx");
      this.oy = this.attr("cy");
      this.attr({opacity: .5});
    },
    function() {// up
      this.attr({opacity: 1});
    }
  );
...
La propiedad .node permite manipular un elemento con jQuery. Sin embargo, tardé un poco en comprender que es mejor usar las funciones de manejo de eventos que provee Raphael, al menos para los gráficos. Resulta más simple y claro.

En el caso del drag, notar que se requieren tres funciones, que definen las acciones para move, start y up (en ese orden). La función que corresponde a move tiene los parámetros dx y dy (los diferenciales de movimiento), y la que corresponde a start tiene los parámetros ox y oy (el punto donde se hace click). Mientras se arrastra el objeto, dx y dy crecen contínuamente, así que implementar el drag no es tan simple como hacer un translate(dx, dy) (hacerlo provoca un desconcertante efecto acelerado), sino que hay que reflexionar un poco, como en el ejemplo, que los suma a la posición inicial.

Para el ejemplo del mapa, tardé varios días en descubrir el modo de usar hover y drag para que funcionaran como quería.

El mapa


La idea es tener el mapa del Perú con sus departamentos, y que estos se resalten al pasar el mouse sobre ellos. Al hacer click en alguno, se dispara alguna acción, por ejemplo mostrar su nombre en una caja.

Puede ver el demo aquí, y descargar el código fuente del proyecto en GitHub aquí.

Raphael se puede usar sólo, pero es de ayuda ayuda usar también jquery.
...
<style type="text/css">
  body {
    background-color: #222;
    color: white;
  }
  #wrapper {
    _position: absolute;
    _top: 50%;
  }
  #container {
    background-color: black;
    width: 700px;
    height: 500px;
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    margin: auto;
    overflow: hide;
    _position: relative;
    _margin: 0 auto;
    _top: -50%;
    _overflow: none;
  }
  .info {
    display: none;
    text-align: center;
    font-family: "Century Gothic", Helvetica, "Bitstream Vera Sans", sans-serif;
    font-size: 24pt;
    line-height: 100px;
  }
  #infobox {
    border: 1px solid #ccc;
    position: absolute;
    top: 150px;
    left: 350px;
    width: 300px;
    height: 100px;
    overlay: auto;
  }
  #test {
    display: none;
    color: #0f0;
  }
</style>
...

<script type="text/javascript">
  $(function() {
    
    //http://www.switchonthecode.com/tutorials/xml-parsing-with-jquery
    $.ajax({
      type: 'GET',
      url: 'images/peru-h500.xml',// .svg renamed .xml for IE support
      dataType: 'xml',
      success: function(xml) {
        var r = Raphael('canvas', 700, 500);
        var map = {};
        var map_set = r.set();
        var active_fill = 'gold';
        var active_stroke = 'white';
        var normal_fill = $('body').css('background-color');
        var normal_stroke = '#ccc';
        var active = null;
        
        $(xml).find('path').each(function() {
          var id = (String)($(this).attr('id'));
          var path = (String)($(this).attr('d'));
          map[id] = r.path(path)
            .attr({fill:normal_fill, stroke: normal_stroke})
            .drag(
              // dx,dy van incrementandose
              // aqui calculo el diferencial continuamente
              function(dx, dy) {// move
                this.translate(dx-this.dx, dy-this.dy);
                this.dx = dx;
                this.dy = dy;
                //$('#test').html(dx+'--'+dy);
              },
              function(ox, oy) {// start
                //this.ox = ox;
                //this.oy = oy;
                this.dx = 0;
                this.dy = 0;
                this.toFront();
                this.attr({opacity: .5});
                //$('#test').html(ox+'-'+oy);
              },
              function() {// up
                // regresa a la posición original
                this.translate(-this.dx, -this.dy);
                this.attr({opacity: 1});
                
                // Este bloque lo hacia en un .click()
                // pero mejor aqui para que tambien funcione en IE


                // restablecer activo previo
                if (active) {
                  active.animate({fill: normal_fill, stroke: normal_stroke}, 500, '>');
                }
                // activar actual
                active = this;
                active.animate({fill: this.color, opacity: 1}, 500, '>');


                // ocultar otras info
                $('.info').hide();
                // mostrar info actual
                $('#'+id).show().css('background-color', this.color);
                
              }
            )
            .hover(function() {
              this.color = Raphael.getColor();
              if (this!=active) {
                this.animate({fill: this.color, stroke: active_stroke}, 500, '>');
              }
            }, function() {
              if (this!=active) {
                this.animate({fill: normal_fill, stroke: normal_stroke}, 500, '>');
              }
            })
          map_set.push(map[id]);
        });// end each
      } // end success
    });
    
  });
</script>  
...

<div id="wrapper">
  <div id="container">
    <div id="canvas"></div>
    <div id="test">[TEST]</div>
    <div id="infobox">
      <div class="info" style="display:block;">Perú</div>
      <div id="Arequipa" class="info">Arequipa</div>
      <div id="Ancash" class="info">Ancash</div>
      <div id="Apurimac" class="info">Apurímac</div>
      <div id="Ica" class="info">Ica</div>
      <div id="Lima" class="info">Lima</div>
      <div id="Ayacucho" class="info">Ayacucho</div>
      <div id="Piura" class="info">Piura</div>
      <div id="Lambayeque" class="info">Lambayeque</div>
      <div id="Tumbes" class="info">Tumbes</div>
      <div id="Tacna" class="info">Tacna</div>
      <div id="Puno" class="info">Puno</div>
      <div id="Huancavelica" class="info">Huancavelica</div>
      <div id="Cuzco" class="info">Cuzco</div>
      <div id="Junin" class="info">Junín</div>
      <div id="Ucayali" class="info">Ucayali</div>
      <div id="Pasco" class="info">Pasco</div>
      <div id="Huanuco" class="info">Huánuco</div>
      <div id="San_Martin" class="info">San Martín</div>
      <div id="Cajamarca" class="info">Cajamarca</div>
      <div id="Amazonas" class="info">Amazonas</div>
      <div id="La_Libertad" class="info">La Libertad</div>
      <div id="Loreto" class="info">Loreto</div>
      <div id="Moquegua" class="info">Moquegua</div>
      <div id="Madre_de_Dios" class="info">Madre de Dios</div>
      <div id="Titicaca" class="info">Lago Titicaca</div>
    </div>
  </div>
</div>
  • En los estilos aparecen #wrapper y #container para la técnica que permite centrar el #container absolutamente en la ventana.
  • También verá un div #test, que uso durante el desarrollo como consola de salida.
  • En un comienzo, copié a mano cada path (el atributo path.d dentro del .svg). Luego me pareció más práctico cargarlos desde un archivo.
    Aquí los he cargado de un archivo svg externo peru-h500.xml
    Originalmente se llamaba peru-h500.svg, pero lo renombré cuando descubrí que IE8 no lo procesaba como .svg.
  • El svg base lo obtuve de Wikipedia. Luego use Inkscape para dejar sólo los paths que necesitaba.
    También corregí los id de los path para que fueran cadenas sin acentos ni espacios en blanco, ya que los iba a usar como claves de los arrays de propiedades que iba a crear.
    Algo especialmente complicado en Inkscape fue lograr que desapareciera la transformación translate() para el grupo de paths, y que en su lugar se aplicara a cada valor.
    Para ser sinceros, no estoy seguro de como lo logré, pero me parece que funcionó cuando desagrupé, seleccioné los paths, y elegí alinearlos respecto a la página (top y left).
    Previamente, para cambiar el tamaño escalé y para cambiar el tamaño del canvas entré a propiedades del documento, fit.
  • Cuando pruebe el demo, arrastre uno de los departamentos para comprobar el efecto del drag.
  • He probado esta aplicación en FF5 (3.6.8), Chrome 6, y IE8.
Para mí es muy interesante las cosas que se pueden hacer con SVG y con Raphael. Ojalá este material le sirva de ayuda.

2010/06/01

Montando HTML sobre un flash

Problema:
Hay un objeto flash que debe funcionar como un botón que abra una ventana html flotante.

Solución:
Para abrir una ventana html flotante usaría window.open().
Encuentro que no es posible asociar a un objeto flash (definido con el tag object),un evento onclick.

Se me ocurre que podría cubrir el objeto flash con un bloque html de las mismas dimensiones al que si pudiera asociar un evento onclick.

Para eso, me baso en un patrón que aparece en Pro CSS and HTML Design Patterns, de Michael Bowers.
Consiste en posicionar absolutamente un div dentro de otro.

Hago que el div banner_container contenga al objeto flash, pero también al div banner_overlay, que lo cubrirá:

<div id="banner_container">
  <div id="banner_overlay">
  </div>
  <object...>...</object>
</div&gt

La clave está en hacer que banner_container tenga position:relative y banner_overlay tenga position:absolute. Además, ambos deben tener las mismas dimensiones del objeto flash, para que lo cubran exactamente.

#banner_container {
    position: relative;
    width: 200px;
    height: 100px;
}
#banner_overlay {
    position: absolute;
    width: 200px;
    height: 100px;
    border: 1px solid #abe;
}

Luego, se puede usar javascript sobre el div banner_overlay. Por ejemplo, con jquery:

$(document).ready(function() {
    $('#banner_overlay').click(function() {
        var w = window.open('ventana1.html', 'Ventana 1', 'width=400,height=300');
    });
});

Puede encontrar un demo aquí.