Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ideas for rendering HTML *within* Raphael (SVG/VML) shapes

I'm working on an application that uses Raphael to draw primitive shapes (rectangles, ellipses, triangles etc) and lines but allows the user to move/resize these objects as well. One of the main requirements is that the face of shapes can have formatted text. The actual text is a subset of Markdown (simple things like bolding, italics, lists) and is rendered as HTML.

FWIW - I'm using Backbone.js views to modularize the shape logic.

Approach 1

My initial thought was to use a combination of foreignObject for SVG and direct HTML with VML for IE. However, IE9 doesn't support foreignObject, and therefore this approach had to be abandoned.

Approach 2

With the beside the canvas object, add divs that contain the actual HTML. Then, position them over the actual shape with a transparent background. I've created a shape view that has references to both the actual Raphael shape and the "overlay" div. There are a couple of problems with this approach:

  1. Using overlay that aren't children of the SVG/VML container feels wrong. Does having this overlay element cause other issues with rendering down the road?

  2. Events that are normally trapped by Raphael (drag, click etc) need to be forwarded from the overlay to the Raphael object. For browsers that support pointer-events, this is easily done:

    div.shape-text-overlay {
      position: absolute;
      background: none;
      pointer-events: none;
    }
    

    However, other browsers (like IE8 and below) need forwarding of the events:

    var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout';
    this.$elText.on(forwardedEvents, function(e) {
    
      var original = e.originalEvent;
    
      var event;
      if (document.createEvent) {
        event = document.createEvent('HTMLEvents');
        event.initEvent(e.type, true, true);
      } 
      else {
        event = document.createEventObject();
        event.eventType = e.type;
      }
    
      // FYI - This is the most simplistic approach to event forwarding. 
      // My real implementation is much larger and uses MouseEvents and UIEvents separately.
    
      event.eventName = e.type;
      _.extend(event, original);
    
      if (document.createEvent) {
        that.el.node.dispatchEvent(event);
      } 
      else {
        that.el.node.fireEvent('on' + event.eventType, event);
      }
    
    });
    
  3. Overlapping shapes cause the text to be overlapped because the text/shapes are on different layers. Although overlapping shapes won't be common, this looks bad:

    overlap

This approach is what I'm currently using but it just feels like a huge hack.

Approach 3

Almost like Approach 1, but this would involve writing text nodes/VML nodes directly. The biggest problem with this is the amount of manual conversion necessary. Breaking outside of Raphael's API seems like it could cause stability issues with the other Raphael objects.

Question

Has anyone else had to do something similar (rendering HTML inside of SVG/VML)? If so, how did you solve this problem? Were events taken into account?

like image 296
TheCloudlessSky Avatar asked May 09 '12 12:05

TheCloudlessSky


1 Answers

I built this project (no longer live) using Raphael. What I did is actually abandoned the idea of using HTML inside of SVG because it was just too messy. Instead, I absolutely positioned an HTML layer on top of the SVG layer and moved them around together. When I wanted the HTML to show, I merely faded it in and faded the corresponding SVG object out. If timed and lined up correctly, it's not really noticeable to the untrained eye.

While this may not necessarily be what you're looking for, maybe it will get your mind thinking of new ways to approach your problem! Feel free to look at the JS on that page, as it is unminified ;)

PS, the project is a lead-gathering application. If you just want to see how it works, select "Friend" in the first dropdown and you don't have to provide any info.

like image 82
Jason Avatar answered Nov 13 '22 08:11

Jason