planit
Version:
Interactive markers on an image or blank canvas
293 lines (261 loc) • 9.97 kB
text/coffeescript
# ======================================================== Setting Image
# Zoom the image out all the way and sets the markers
# appropriately
#
resetImage: =>
@imagePosition =
leftPx: 0
topPx: 0
width: @image.width()
height: @image.height()
scale: 1
increment: 0.5
setBackground.call(@)
true
# (private) Moves the background and markers without
# animation to the location set by the imagePosition
# property
#
setBackground = ->
@image.css
left: "#{@imagePosition.leftPx}px"
top: "#{@imagePosition.topPx}px"
width: "#{@imagePosition.scale * 100.0}%"
height: 'auto'
setMarkers.call(@)
# (private) Equivalent to setBackground, but with
# animation
#
animateBackground = ->
@image.animate
left: "#{@imagePosition.leftPx}px"
top: "#{@imagePosition.topPx}px"
width: "#{@imagePosition.scale * 100.0}%"
height: 'auto'
, 250
animateMarkers.call(@)
# ======================================================== Setting Markers
# (private) Sets markers in correct location, based on
# image position
#
setMarkers = ->
markers = @container.find(".#{Planit.markerClass}")
if markers.length > 0
for marker in markers
left = (@calc(imgWidth) * ($(marker).attr('data-xPc') / 100)) +
@imagePosition.leftPx - ($(marker).outerWidth() / 2)
top = (@calc(imgHeight) * ($(marker).attr('data-yPc') / 100)) +
@imagePosition.topPx - ($(marker).outerHeight() / 2)
$(marker).css
left: "#{left}px"
top: "#{top}px"
positionInfoboxes.call(@)
# (private) Equivalent to setMarkers, but with animation
#
animateMarkers = ->
markers = @container.find(".#{Planit.markerClass}")
if markers.length > 0
for marker in markers
m = new Planit.Marker(@, $(marker).attr('data-marker'))
m.hideInfobox()
left = (@calc(imgWidth) * ($(marker).attr('data-xPc') / 100)) +
@imagePosition.leftPx - ($(marker).outerWidth() / 2)
top = (@calc(imgHeight) * ($(marker).attr('data-yPc') / 100)) +
@imagePosition.topPx - ($(marker).outerHeight() / 2)
do (m) ->
$(marker).animate
left: "#{left}px"
top: "#{top}px"
, 250, () =>
m.positionInfobox()
m.unhideInfobox()
# ======================================================== Setting Infoboxes
# (private) Appropriately position the infobox on every
# marker, the logic for which is in the Marker class
#
positionInfoboxes = ->
for marker in @container.find(".#{Planit.markerClass}")
m = new Planit.Marker(@, $(marker).attr('data-marker'))
m.positionInfobox()
true
# ======================================================== Move Actions
# Will center the image on the given coordinates as [x,y]
# in floated percentages. Ensures there is enough image on
# each side by zooming in if necessary.
#
centerOn: (coords) =>
if coords[0] >= 50 then x = 100 - coords[0] else x = coords[0]
if coords[1] >= 50 then y = 100 - coords[1] else y = coords[1]
wMin = 50 * (@calc(containerWidth) / x)
hMin = 50 * (@calc(containerHeight) / y)
# hides other active infoboxes, but will still show
# this infobox
@container.find(".#{Planit.infoboxClass}").removeClass('active')
# Get our initial position
@imagePosition.leftPx = - (
(@calc(imgWidth) * (coords[0] / 100)) - (@calc(containerWidth) / 2)
)
@imagePosition.topPx = - (
(@calc(imgHeight) * (coords[1] / 100)) - (@calc(containerHeight) / 2)
)
# keep theoretically making the image bigger until it is
# large enough to center on our point
while (@calc(imgWidth) < wMin) || (@calc(imgHeight) < hMin)
@imagePosition.scale = @imagePosition.scale + @imagePosition.increment
@imagePosition.leftPx = - (
(@calc(imgWidth) * (coords[0] / 100)) - (@calc(containerWidth) / 2)
)
@imagePosition.topPx = - (
(@calc(imgHeight) * (coords[1] / 100)) - (@calc(containerHeight) / 2)
)
animateBackground.call(@)
coords
# Zooms the image to a specific "level" which is an
# incremented integer starting at zero
#
zoomTo: (level) =>
i = @imagePosition.increment
unless ((level * i) + 1) == @imagePosition.scale
@imagePosition.scale = (level * i) + 1 + i
@zoomOut()
level
# ======================================================== Calculations
# Method for accessing the private calculation methods
#
calc: (method) =>
method.call(@)
# (private) Width of the image
#
imgWidth = ->
parseFloat(@imagePosition.width * @imagePosition.scale)
# (private) The number of pixels added with each zoom level
#
imgWidthClickIncrement = ->
parseFloat(@imagePosition.width * @imagePosition.increment)
# (private) The width of the container
#
containerWidth = ->
parseFloat(@markersContainer.width())
# (private) Number of pixels left side of image is from
# left side of the container
#
imgOffsetLeft = ->
Math.abs(parseFloat(@image.css('left')))
# (private) Height of the image
#
imgHeight = ->
parseFloat(@imagePosition.height * @imagePosition.scale)
# (private) The number of pixels added or removed with
# each zoom level
#
imgHeightClickIncrement = ->
parseFloat(@imagePosition.height * @imagePosition.increment)
# (private) The height of the container (pixels)
#
containerHeight = ->
parseFloat(@markersContainer.height())
# (private) The number of pixels the top of the image is
# from the top of the container
#
imgOffsetTop = ->
Math.abs(parseFloat(@image.css('top')))
# (private) Coordinates of an event as a percentage of the
# dimensions of the container, relative to the top left
# corner of the container
#
zEventPosition = (e) ->
left: (e.pageX - @container.offset().left) / @calc(containerWidth)
top: (e.pageY - @container.offset().top) / @calc(containerHeight)
# ======================================================== Events
# (private) Listener for double-clicking on the plan
#
zDblClick = (e) ->
if $(e.target).attr('data-zoom-id') == @zoomId
click = zEventPosition.call(@, e)
@zoomIn('click', click.left, click.top)
# (private) Listener for the start of a click on the plan
#
zMouseDown = (e) ->
if $(e.target).attr('data-zoom-id') == @zoomId && e.which == 1
@isDragging = true
coords = zEventPosition.call(@, e)
@dragCoords =
pointRef: coords
imgRef:
left: 0 - @calc(imgOffsetLeft)
top: 0 - @calc(imgOffsetTop)
max:
right: (coords.left * @calc(containerWidth)) + @calc(imgOffsetLeft)
left: (coords.left * @calc(containerWidth)) - (@calc(imgWidth) -
(@calc(containerWidth) + @calc(imgOffsetLeft)))
bottom: (coords.top * @calc(containerHeight)) + @calc(imgOffsetTop)
top: (coords.top * @calc(containerHeight)) - (@calc(imgHeight) -
(@calc(containerHeight) + @calc(imgOffsetTop)))
true
# (private) Listener for when the mouse moves anywhere on
# the document
#
zMouseMove = (e) ->
if @isDragging
coords = zEventPosition.call(@, e)
dragLeft = coords.left * @calc(containerWidth)
dragTop = coords.top * @calc(containerHeight)
if dragLeft >= @dragCoords.max.left && dragLeft <= @dragCoords.max.right
left = (coords.left - @dragCoords.pointRef.left) * @calc(containerWidth)
@imagePosition.leftPx = @dragCoords.imgRef.left + left
else if dragLeft < @dragCoords.max.left
@imagePosition.leftPx = @calc(containerWidth) - @calc(imgWidth)
else if dragLeft > @dragCoords.max.right
@imagePosition.leftPx = 0
if dragTop >= @dragCoords.max.top && dragTop <= @dragCoords.max.bottom
top = (coords.top - @dragCoords.pointRef.top) * @calc(containerHeight)
@imagePosition.topPx = @dragCoords.imgRef.top + top
else if dragTop < @dragCoords.max.top
@imagePosition.topPx = @calc(containerHeight) - @calc(imgHeight)
else if dragTop > @dragCoords.max.bottom
@imagePosition.topPx = 0
setBackground.call(@)
true
# (private) Listener for the end of a click anywhere on
# the document
#
zMouseUp = (e) ->
@isDragging = false
positionInfoboxes.call(@)
true
# ======================================================== Zooming
# Takes current zoom position and zooms in to the center
# one level deeper
#
zoomIn: =>
@imagePosition.scale = @imagePosition.scale + @imagePosition.increment
@imagePosition.leftPx = - @calc(imgOffsetLeft) -
(@calc(imgWidthClickIncrement) / 2)
@imagePosition.topPx = - @calc(imgOffsetTop) -
(@calc(imgHeightClickIncrement) / 2)
animateBackground.call(@)
true
# Zooms out one level. Attempts to zoom out from the
# center, but will adjust based on available image space.
#
zoomOut: () =>
if @imagePosition.scale > 1
@imagePosition.scale = @imagePosition.scale - @imagePosition.increment
leftPx = - @calc(imgOffsetLeft) + (@calc(imgWidthClickIncrement) / 2)
topPx = - @calc(imgOffsetTop) + (@calc(imgHeightClickIncrement) / 2)
if leftPx > 0
@imagePosition.leftPx = 0
else if leftPx < @calc(containerWidth) - @calc(imgWidth)
@imagePosition.leftPx = @calc(containerWidth) - @calc(imgWidth)
else
@imagePosition.leftPx = leftPx
if topPx > 0
@imagePosition.topPx = 0
else if topPx < @calc(containerHeight) - @calc(imgHeight)
@imagePosition.topPx = @calc(containerHeight) - @calc(imgHeight)
else
@imagePosition.topPx = topPx
animateBackground.call(@)
true
else
false