7
\$\begingroup\$

The code below is loading an svg into the canvas via RaphaelJS. There are mouseover/mouseout events, on click on an element of the svg, and drag/zoom.

I would like to know, what are some good practices, or how to improve the productivity and maybe shorten my code. One issues may be that I use a lot of iterations through the elements(~2000-3000) on the svg image. This is not the only "problem" in my code, so if anyone has any ideas, I am ready to try them.

Raphael.fn.select = (selector) ->
  type     = selector.charAt(0)
  selector = selector.substr(1)
  attr     = if type == '.' then 'class' else 'id'
  els      = []
  bot      = this.bottom
  while bot
    if bot.node.getAttribute(attr) == selector
      els.push(bot)
    bot = bot.next
  return if type == '.' then els else els[0] or null

;((window) ->
  'use strict'

  loadSVG = (file) ->
    response = null
    $.ajax({
      type: 'GET',
      url: file,
      dataType: 'xml',
      async: false,
      success: (svgXML) ->
        response = svgXML
    })
    return response

  selectedPoints = []
  disabledPoints = ['seggiolino_1637', 'seggiolino_1881', 'seggiolino_1984', 'seggiolino_889', 'seggiolino_891', 'seggiolino_893', 'seggiolino_890', 'seggiolino_892', 'seggiolino_894']

  settings =
    clickAnimTime: 0
    hoverScale: 2
    animTime: 300
    animEase: 'backOut'
    scale: 3
    vb:
      x: 0
      y: 0
      width: 1606
      height: 1039
    paper:
      width: 800
      height: 480

  states =
    default:
      transform: ''
      fill: '#008000'
      'stroke-width': 0
    hover:
      transform: 's' + settings.hoverScale
    disabled:
      fill: '#eaeaea'
      'stroke-width': 1
      stroke: '#333333'
    selected:
      transform: 's' + settings.hoverScale
      fill: '#AA8834'
      'stroke-width': 2
      stroke: '#AA8834'

  container = document.getElementById('mapContainer')
  paper     = new Raphael(container, settings.vb.width, settings.vb.height)
  map       = paper.importSVG(loadSVG('img/svgscala.svg'))
  draggable = container.getElementsByTagName('svg')[0]
  paper.setSize(settings.paper.width, settings.paper.height)
  viewBoxWidth  = settings.vb.width
  viewBoxHeight = settings.vb.height
  dx = 0
  dy = 0
  oX = 0
  oY = 0
  oWidth  = viewBoxWidth
  oHeight = viewBoxHeight
  mousedown = false
  startX = 0
  startY = 0
  viewBox = paper.setViewBox(oX, oY, viewBoxWidth, viewBoxHeight, true)
  viewBox.x = oX
  viewBox.y = oY
  zoomLevel = 0
  firedCallback = false

  paper.select('#' + point).attr(states.disabled) for point in disabledPoints

  map.hover(() ->
    if disabledPoints.indexOf(this.node.getAttribute('id')) == -1
      this[0].style.cursor = 'pointer'
      this.animate(states.hover, settings.clickAnimTime, settings.animEase)
    else
      this[0].style.cursor = 'not-allowed'
  , () ->
    if disabledPoints.indexOf(this.node.getAttribute('id')) == -1
      if selectedPoints.indexOf(this.node.getAttribute('id')) == -1
        this.animate(states.default, settings.clickAnimTime, settings.animEase)
  )

  map.click(() ->
    cx = this.attr('cx')
    cy = this.attr('cy')

    if disabledPoints.indexOf(this.node.getAttribute('id')) == -1
      if selectedPoints.indexOf(this.node.getAttribute('id')) == -1
        selectedPoints.push(this.node.getAttribute('id'))
        this.animate(states.selected, settings.clickAnimTime, settings.animEase)
      else
        removeId = selectedPoints.indexOf(this.node.getAttribute('id'))
        selectedPoints.splice(removeId, 1)
        this.animate(states.default, settings.clickAnimTime, settings.animEase)
  )

  wheel = (e) ->
    e = e or window.event
    delta = 0

    if e.wheelDelta
      delta = e.wheelDelta / 120
    else if e.detail
      delta = -e.detail / 3

    if delta then handle(e, delta)
    handleAreas()
    if e.preventDefault then e.preventDefault()

    return e.returnValue = false

  handle = (e, delta) ->
    vBOw = viewBoxWidth
    vBOh = viewBoxHeight
    factor = 0

    if delta > 0
      zoomLevel++
      viewBoxWidth  *= 0.95
      viewBoxHeight *= 0.95
      factor = 0.95
    else
      zoomLevel--
      viewBoxWidth  *= 1.05
      viewBoxHeight *= 1.05
      factor = 1.05

    viewBox.x -= (e.pageX - (viewBoxWidth - vBOw)) * (factor - 1)
    viewBox.y -= (e.pageY - (viewBoxHeight - vBOh)) * (factor - 1)
    paper.setViewBox(viewBox.x, viewBox.y, viewBoxWidth, viewBoxHeight)

  draggable.onmousedown = (e) ->
    if paper.getElementByPoint(e.pageX, e.pageY) != null
      return
    e = e or window.event
    mousedown = true
    startX = e.pageX
    startY = e.pageY

  document.onmousemove = (e) ->
    if mousedown == false then return
    document.body.style.cursor = '-webkit-grabbing'
    e  = e or window.event
    dx = startX - e.pageX
    dy = startY - e.pageY
    x  = viewBoxWidth / paper.width
    y  = viewBoxHeight / paper.height
    dx *= x
    dy *= y

    paper.setViewBox(viewBox.x + dx, viewBox.y + dy, viewBoxWidth, viewBoxHeight)

  document.onmouseup = (e) ->
    document.body.style.cursor = 'initial'
    if mousedown == false then return
    e = e or window.event
    viewBox.x += dx
    viewBox.y += dy
    mousedown  = false

  handleAreas = () ->
    if zoomLevel > 30
      if not firedCallback
        firedCallback = true
        a.attr('fill', '') for a in paper.select('.area')
        console.log 'zoomed in'
    else
      if firedCallback
        firedCallback = false
        a.attr('fill', 'red') for a in paper.select('.area')

  draggable.addEventListener('DOMMouseScroll', wheel, false)
  draggable.onmousewheel = draggable.onmousewheel = wheel
)(window)

Maybe some of you know a shorthand expression in CoffeeScript, or maybe you can suggest a nice trick in JS. The code works fine, but I won't say I'm happy with it. I will post the code in compiled JavaScript if someone needs it.

\$\endgroup\$
1
\$\begingroup\$
wheel = (e) ->
    e = e or window.event
    # the code follows

Here you want to set argument e to some default value if e is missing. CoffeeScript allows you to set default value of an argument right in the function signature:

wheel = (e = window.event) ->
    # the code follows

Also there are some alternative solutions (perhaps more favorable in CoffeeScript):

e ||= window.event
# sets e to window.event if e evaluates to false
# null, unefined, 0, "", NaN and false values will be replaced
# same as `e || (e = window.event)`

and

e ?= window.event
# sets e to window.event only if e is null or undefined
# 0, "" and other falsy friends are allowed
# same as `if (e == null) { e = window.event }`

The operator ? in the latter form is special. It is called The Existential Operator. I guess it is the prefered way to set a variable to default value or test for it existence.


You can omit () from () -> function signature since there are no arguments. The possible shortest function signature in CoffeeScript is ->, same as function() {} in JavaScript.


if e.preventDefault then e.preventDefault()

may be written as

e.preventDefault?()

Notice the ? operator before (). It will test if e.preventDefault exists and is actually a function before running it.


if delta then handle(e, delta)

may be

handle(e, delta) if delta
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.