Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Split the crop box control into a module

  • Loading branch information...
commit 66082018ddd3a442799ba659651326fb9d1237af 1 parent ba82ef8
@caleb authored
Showing with 383 additions and 378 deletions.
  1. +382 −0 lib/crop-box.coffee
  2. +1 −378 lib/rodeo-crop.coffee
View
382 lib/crop-box.coffee
@@ -0,0 +1,382 @@
+`import _ from "funderscore"`
+`import drawing from "drawing"`
+
+class CropBox extends drawing.Drawable
+ constructor: (options) ->
+ super _.extend
+ dragable: true
+ , options
+
+ @image = options.image
+ @handleSize = options.handleSize || 10
+ @screenStyle = options.screenStyle || 'rgba(0, 0, 0, .75)'
+
+ @topScreen = new drawing.Rectangle fillStyle: @screenStyle
+ @leftScreen = new drawing.Rectangle fillStyle: @screenStyle
+ @rightScreen = new drawing.Rectangle fillStyle: @screenStyle
+ @bottomScreen = new drawing.Rectangle fillStyle: @screenStyle
+
+ @cropX = options.cropX || 0
+ @cropY = options.cropY || 0
+ @cropWidth = options.cropWidth || @handleSize * 4
+ @cropHeight = options.cropHeight || @handleSize * 4
+
+ @onCropFrameChanged = options.onCropFrameChanged || null
+
+ @dragging = null
+
+ @handles = {}
+
+ frame: () ->
+ {
+ x: if @w < 0 then @x + @w else @x
+ y: if @h < 0 then @y + @h else @y
+ w: Math.abs @w
+ h: Math.abs @h
+ }
+
+ cropFrame: () ->
+ {
+ x: @cropX
+ y: @cropY
+ width: @cropWidth
+ height: @cropHeight
+ }
+
+ updateCropAreaFromFrame: () ->
+ frame = @frame()
+ naturalBounds = @image.naturalBounds()
+ imageBounds = @image.bounds()
+
+ if imageBounds.w && imageBounds.h
+ @cropX = (naturalBounds.w * (frame.x / imageBounds.w))
+ @cropY = (naturalBounds.h * (frame.y / imageBounds.h))
+ @cropWidth = (naturalBounds.w * (frame.w / imageBounds.w))
+ @cropHeight = (naturalBounds.h * (frame.h / imageBounds.h))
+
+ @onCropFrameChanged? @cropFrame()
+
+ setFrameAndUpdateCropArea: (frame) ->
+ @x = frame.x
+ @y = frame.y
+ @w = frame.w
+ @h = frame.h
+
+ @updateCropAreaFromFrame()
+
+ updateFrameFromCropArea: () ->
+ naturalBounds = @image.naturalBounds()
+ imageBounds = @image.bounds()
+
+ if imageBounds.w && imageBounds.h
+ @x = (imageBounds.w * (@cropX / naturalBounds.w))
+ @y = (imageBounds.h * (@cropY / naturalBounds.h))
+ @w = (imageBounds.w * (@cropWidth / naturalBounds.w))
+ @h = (imageBounds.h * (@cropHeight / naturalBounds.h))
+
+ setCropAreaAndUpdateFrame: (cropArea) ->
+ naturalBounds = @image.naturalBounds()
+
+ @cropX = Math.min(Math.max(cropArea.x, 0.0), naturalBounds.w)
+ @cropY = Math.min(Math.max(cropArea.y, 0.0), naturalBounds.h)
+ @cropWidth = Math.min(Math.max(cropArea.width, 0.0), naturalBounds.w - @cropX)
+ @cropHeight = Math.min(Math.max(cropArea.height, 0.0), naturalBounds.h - @cropY)
+
+ @updateFrameFromCropArea()
+
+ bounds: () ->
+ {
+ x: 0
+ y: 0
+ w: Math.abs @w
+ h: Math.abs @h
+ }
+
+ onCanvasSizeChange: () ->
+ # move/size the crop area based on the image size
+ @updateFrameFromCropArea()
+
+ containsCanvasPoint: (point) ->
+ local = @convertFromCanvas point
+ containsPoint = @containsPoint local
+ return containsPoint if containsPoint
+
+ # if the body doesn't contain the point, check the handles, which are rendered partly outside the bounds
+ # of the crop tool
+ for direction, handle of @handles
+ return true if handle.containsCanvasPoint point
+
+ return false
+
+ onMouseOut: (point) ->
+ @canvas.style.cursor = 'default'
+
+ onMouseMove: (point) ->
+ for direction, handle of @handles
+ if handle.containsCanvasPoint point
+ switch direction
+ when 'tl'
+ @canvas.style.cursor = 'nw-resize'
+ when 'tm'
+ @canvas.style.cursor = 'n-resize'
+ when 'tr'
+ @canvas.style.cursor = 'ne-resize'
+ when 'ml'
+ @canvas.style.cursor = 'w-resize'
+ when 'mr'
+ @canvas.style.cursor = 'e-resize'
+ when 'bl'
+ @canvas.style.cursor = 'sw-resize'
+ when 'bm'
+ @canvas.style.cursor = 's-resize'
+ when 'br'
+ @canvas.style.cursor = 'se-resize'
+ return
+
+ @canvas.style.cursor = 'move'
+
+ constrainPointInParent: (point) ->
+ {
+ x: Math.min Math.max(point.x, 0), @parent.frame().w
+ y: Math.min Math.max(point.y, 0), @parent.frame().h
+ }
+
+ onMouseDown: (point) ->
+ onMouseUp: (point) ->
+ @dragging = null
+ onDragStart: (point) ->
+ for direction, handle of @handles
+ if handle.containsCanvasPoint point
+ localPoint = handle.convertFromCanvas point
+ @dragging =
+ resizeDirection: direction
+ object: handle
+ offsetX: localPoint.x
+ offsetY: localPoint.y
+ return
+
+ localPoint = @convertFromCanvas point
+
+ @dragging =
+ object: @
+ offsetX: localPoint.x
+ offsetY: localPoint.y
+
+ onDragMove: (point) ->
+ if @dragging?.object == @
+ # move the whole crop area
+ localPoint = @convertFromCanvas point
+ @moveTo
+ x: localPoint.x - @dragging.offsetX
+ y: localPoint.y - @dragging.offsetY
+
+ @updateCropAreaFromFrame()
+ else if @dragging?.resizeDirection
+ parentPoint = @parent.convertFromCanvas point
+
+ switch @dragging.resizeDirection
+ when 'tl'
+ point = @constrainPointInParent parentPoint
+ @w = @w + (@x - point.x)
+ @h = @h + (@y - point.y)
+ @x = point.x
+ @y = point.y
+ when 'tm'
+ point = @constrainPointInParent parentPoint
+ @w = @w
+ @h = @h + (@y - point.y)
+ @x = @x
+ @y = point.y
+ when 'tr'
+ point = @constrainPointInParent parentPoint
+ @w = (point.x - @x)
+ @h = @h + (@y - point.y)
+ @x = @x
+ @y = point.y
+ when 'ml'
+ point = @constrainPointInParent parentPoint
+ @w = @w + (@x - point.x)
+ @h = @h
+ @x = point.x
+ @y = @y
+ when 'mr'
+ point = @constrainPointInParent parentPoint
+ @w = (point.x - @x)
+ @h = @h
+ @x = @x
+ @y = @y
+ when 'bl'
+ point = @constrainPointInParent parentPoint
+ @w = @w + (@x - point.x)
+ @h = (point.y - @y)
+ @x = point.x
+ @y = @y
+ when 'bm'
+ point = @constrainPointInParent parentPoint
+ @w = @w
+ @h = (point.y - @y)
+ @x = @x
+ @y = @y
+ when 'br'
+ point = @constrainPointInParent parentPoint
+ @w = (point.x - @x)
+ @h = (point.y - @y)
+ @x = @x
+ @y = @y
+
+ @updateCropAreaFromFrame()
+
+ onDragEnd: (point) ->
+ # reset our frame after a drag to fix negative widths/heights used during
+ # the dragging process
+ frame = @frame()
+ @x = frame.x
+ @y = frame.y
+ @w = frame.w
+ @h = frame.h
+
+ onClick: (point) ->
+
+ moveTo: (point) ->
+ pos = @convertToParent point
+
+ x = Math.max 0, pos.x
+ y = Math.max 0, pos.y
+ x = Math.min @parent.bounds().w - @w, x
+ y = Math.min @parent.bounds().h - @h, y
+
+ @x = x
+ @y = y
+
+ drawScreen: (ctx) ->
+ frame = @frame()
+ frame.x = Math.round frame.x
+ frame.y = Math.round frame.y
+ frame.w = Math.round frame.w
+ frame.h = Math.round frame.h
+
+ @topScreen.set
+ parent: @parent
+ x: 0
+ y: 0
+ w: @parent.w
+ h: frame.y
+
+ @bottomScreen.set
+ parent: @parent
+ x: 0
+ y: frame.y + frame.h
+ w: @parent.w
+ h: @parent.h - (frame.y + frame.h)
+
+ @leftScreen.set
+ parent: @parent
+ x: 0
+ y: frame.y
+ w: frame.x
+ h: frame.h
+
+ @rightScreen.set
+ parent: @parent
+ x: frame.x + frame.w
+ y: frame.y
+ w: @parent.w - (frame.x + frame.w)
+ h: frame.h
+
+ @topScreen.draw ctx
+ @leftScreen.draw ctx
+ @rightScreen.draw ctx
+ @bottomScreen.draw ctx
+
+ drawHandles: (ctx) ->
+ frame = @frame()
+ frame.x = Math.round frame.x
+ frame.y = Math.round frame.y
+ frame.w = Math.round frame.w
+ frame.h = Math.round frame.h
+
+ newRect = (x, y) =>
+ return new drawing.Rectangle
+ parent: @
+ x: x - (@handleSize / 2) - 0.5
+ y: y - (@handleSize / 2) - 0.5
+ w: @handleSize
+ h: @handleSize
+ lineWidth: 1
+ strokeStyle: 'rgba(192, 192, 192, 1)'
+ fillStyle: 'rgba(64, 64, 64, 1)'
+
+ @handles["tl"] = newRect 0, 0
+ @handles["tm"] = newRect (frame.w / 2), 0
+ @handles["tr"] = newRect frame.w, 0
+
+ @handles["ml"] = newRect 0, (frame.h / 2)
+ @handles["mr"] = newRect frame.w, (frame.h / 2)
+
+ @handles["bl"] = newRect 0, frame.h
+ @handles["bm"] = newRect (frame.w / 2), frame.h
+ @handles["br"] = newRect frame.w, frame.h
+
+ for direction, handle of @handles
+ handle.draw ctx
+
+ drawCropLines: (ctx) ->
+ frame = @frame()
+ frame.x = Math.round frame.x
+ frame.y = Math.round frame.y
+ frame.w = Math.round frame.w
+ frame.h = Math.round frame.h
+
+ opacity = "0.5"
+ lineDash = 8
+
+ @isolateAndMoveToParent ctx, (ctx) =>
+ ctx.beginPath()
+ ctx.strokeStyle = "rgba(255,255,255,#{opacity})"
+ ctx.rect 0.5, 0.5, frame.w, frame.h
+ ctx.closePath()
+ ctx.stroke()
+
+ ctx.beginPath()
+ ctx.strokeStyle = "rgba(0,0,0,#{opacity})"
+ ctx.setLineDash [lineDash]
+ ctx.rect 0.5, 0.5, frame.w, frame.h
+ ctx.closePath()
+ ctx.stroke()
+
+ for x in [ frame.w / 3 + 0.5, (frame.w / 3) * 2 + 0.5 ]
+ ctx.beginPath()
+ ctx.moveTo x, 0
+ ctx.strokeStyle = "rgba(255,255,255,#{opacity})"
+ ctx.setLineDash []
+ ctx.lineTo x, frame.h
+ ctx.stroke()
+
+ ctx.beginPath()
+ ctx.moveTo x, 0
+ ctx.strokeStyle = "rgba(0,0,0,#{opacity})"
+ ctx.setLineDash [lineDash]
+ ctx.lineTo x, frame.h
+ ctx.stroke()
+
+ for y in [ frame.h / 3 + 0.5, (frame.h / 3) * 2 + 0.5 ]
+ ctx.beginPath()
+ ctx.moveTo 0, y
+ ctx.strokeStyle = "rgba(255,255,255,#{opacity})"
+ ctx.setLineDash []
+ ctx.lineTo frame.w, y
+ ctx.stroke()
+
+ ctx.beginPath()
+ ctx.moveTo 0, y
+ ctx.strokeStyle = "rgba(0,0,0,#{opacity})"
+ ctx.setLineDash [lineDash]
+ ctx.lineTo frame.w, y
+ ctx.stroke()
+
+ draw: (ctx) ->
+ @drawScreen ctx
+ @drawCropLines ctx
+ @drawHandles ctx
+
+`export default CropBox`
View
379 lib/rodeo-crop.coffee
@@ -1,386 +1,9 @@
`import _ from "funderscore"`
`import drawing from "drawing"`
+`import CropBox from "crop-box"`
RodeoCrop = {}
-class CropBox extends drawing.Drawable
- constructor: (options) ->
- super _.extend
- dragable: true
- , options
-
- @image = options.image
- @handleSize = options.handleSize || 10
- @screenStyle = options.screenStyle || 'rgba(0, 0, 0, .75)'
-
- @topScreen = new drawing.Rectangle fillStyle: @screenStyle
- @leftScreen = new drawing.Rectangle fillStyle: @screenStyle
- @rightScreen = new drawing.Rectangle fillStyle: @screenStyle
- @bottomScreen = new drawing.Rectangle fillStyle: @screenStyle
-
- @cropX = options.cropX || 0
- @cropY = options.cropY || 0
- @cropWidth = options.cropWidth || @handleSize * 4
- @cropHeight = options.cropHeight || @handleSize * 4
-
- @onCropFrameChanged = options.onCropFrameChanged || null
-
- @dragging = null
-
- @handles = {}
-
- frame: () ->
- {
- x: if @w < 0 then @x + @w else @x
- y: if @h < 0 then @y + @h else @y
- w: Math.abs @w
- h: Math.abs @h
- }
-
- cropFrame: () ->
- {
- x: @cropX
- y: @cropY
- width: @cropWidth
- height: @cropHeight
- }
-
- updateCropAreaFromFrame: () ->
- frame = @frame()
- naturalBounds = @image.naturalBounds()
- imageBounds = @image.bounds()
-
- if imageBounds.w && imageBounds.h
- @cropX = (naturalBounds.w * (frame.x / imageBounds.w))
- @cropY = (naturalBounds.h * (frame.y / imageBounds.h))
- @cropWidth = (naturalBounds.w * (frame.w / imageBounds.w))
- @cropHeight = (naturalBounds.h * (frame.h / imageBounds.h))
-
- @onCropFrameChanged? @cropFrame()
-
- setFrameAndUpdateCropArea: (frame) ->
- @x = frame.x
- @y = frame.y
- @w = frame.w
- @h = frame.h
-
- @updateCropAreaFromFrame()
-
- updateFrameFromCropArea: () ->
- naturalBounds = @image.naturalBounds()
- imageBounds = @image.bounds()
-
- if imageBounds.w && imageBounds.h
- @x = (imageBounds.w * (@cropX / naturalBounds.w))
- @y = (imageBounds.h * (@cropY / naturalBounds.h))
- @w = (imageBounds.w * (@cropWidth / naturalBounds.w))
- @h = (imageBounds.h * (@cropHeight / naturalBounds.h))
-
- setCropAreaAndUpdateFrame: (cropArea) ->
- naturalBounds = @image.naturalBounds()
-
- @cropX = Math.min(Math.max(cropArea.x, 0.0), naturalBounds.w)
- @cropY = Math.min(Math.max(cropArea.y, 0.0), naturalBounds.h)
- @cropWidth = Math.min(Math.max(cropArea.width, 0.0), naturalBounds.w - @cropX)
- @cropHeight = Math.min(Math.max(cropArea.height, 0.0), naturalBounds.h - @cropY)
-
- @updateFrameFromCropArea()
-
- bounds: () ->
- {
- x: 0
- y: 0
- w: Math.abs @w
- h: Math.abs @h
- }
-
- onCanvasSizeChange: () ->
- # move/size the crop area based on the image size
- @updateFrameFromCropArea()
-
- containsCanvasPoint: (point) ->
- local = @convertFromCanvas point
- containsPoint = @containsPoint local
- return containsPoint if containsPoint
-
- # if the body doesn't contain the point, check the handles, which are rendered partly outside the bounds
- # of the crop tool
- for direction, handle of @handles
- return true if handle.containsCanvasPoint point
-
- return false
-
- onMouseOut: (point) ->
- @canvas.style.cursor = 'default'
-
- onMouseMove: (point) ->
- for direction, handle of @handles
- if handle.containsCanvasPoint point
- switch direction
- when 'tl'
- @canvas.style.cursor = 'nw-resize'
- when 'tm'
- @canvas.style.cursor = 'n-resize'
- when 'tr'
- @canvas.style.cursor = 'ne-resize'
- when 'ml'
- @canvas.style.cursor = 'w-resize'
- when 'mr'
- @canvas.style.cursor = 'e-resize'
- when 'bl'
- @canvas.style.cursor = 'sw-resize'
- when 'bm'
- @canvas.style.cursor = 's-resize'
- when 'br'
- @canvas.style.cursor = 'se-resize'
- return
-
- @canvas.style.cursor = 'move'
-
- constrainPointInParent: (point) ->
- {
- x: Math.min Math.max(point.x, 0), @parent.frame().w
- y: Math.min Math.max(point.y, 0), @parent.frame().h
- }
-
- onMouseDown: (point) ->
- onMouseUp: (point) ->
- @dragging = null
- onDragStart: (point) ->
- for direction, handle of @handles
- if handle.containsCanvasPoint point
- localPoint = handle.convertFromCanvas point
- @dragging =
- resizeDirection: direction
- object: handle
- offsetX: localPoint.x
- offsetY: localPoint.y
- return
-
- localPoint = @convertFromCanvas point
-
- @dragging =
- object: @
- offsetX: localPoint.x
- offsetY: localPoint.y
-
- onDragMove: (point) ->
- if @dragging?.object == @
- # move the whole crop area
- localPoint = @convertFromCanvas point
- @moveTo
- x: localPoint.x - @dragging.offsetX
- y: localPoint.y - @dragging.offsetY
-
- @updateCropAreaFromFrame()
- else if @dragging?.resizeDirection
- parentPoint = @parent.convertFromCanvas point
-
- switch @dragging.resizeDirection
- when 'tl'
- point = @constrainPointInParent parentPoint
- @w = @w + (@x - point.x)
- @h = @h + (@y - point.y)
- @x = point.x
- @y = point.y
- when 'tm'
- point = @constrainPointInParent parentPoint
- @w = @w
- @h = @h + (@y - point.y)
- @x = @x
- @y = point.y
- when 'tr'
- point = @constrainPointInParent parentPoint
- @w = (point.x - @x)
- @h = @h + (@y - point.y)
- @x = @x
- @y = point.y
- when 'ml'
- point = @constrainPointInParent parentPoint
- @w = @w + (@x - point.x)
- @h = @h
- @x = point.x
- @y = @y
- when 'mr'
- point = @constrainPointInParent parentPoint
- @w = (point.x - @x)
- @h = @h
- @x = @x
- @y = @y
- when 'bl'
- point = @constrainPointInParent parentPoint
- @w = @w + (@x - point.x)
- @h = (point.y - @y)
- @x = point.x
- @y = @y
- when 'bm'
- point = @constrainPointInParent parentPoint
- @w = @w
- @h = (point.y - @y)
- @x = @x
- @y = @y
- when 'br'
- point = @constrainPointInParent parentPoint
- @w = (point.x - @x)
- @h = (point.y - @y)
- @x = @x
- @y = @y
-
- @updateCropAreaFromFrame()
-
- onDragEnd: (point) ->
- # reset our frame after a drag to fix negative widths/heights used during
- # the dragging process
- frame = @frame()
- @x = frame.x
- @y = frame.y
- @w = frame.w
- @h = frame.h
-
- onClick: (point) ->
-
- moveTo: (point) ->
- pos = @convertToParent point
-
- x = Math.max 0, pos.x
- y = Math.max 0, pos.y
- x = Math.min @parent.bounds().w - @w, x
- y = Math.min @parent.bounds().h - @h, y
-
- @x = x
- @y = y
-
- drawScreen: (ctx) ->
- frame = @frame()
- frame.x = Math.round frame.x
- frame.y = Math.round frame.y
- frame.w = Math.round frame.w
- frame.h = Math.round frame.h
-
- @topScreen.set
- parent: @parent
- x: 0
- y: 0
- w: @parent.w
- h: frame.y
-
- @bottomScreen.set
- parent: @parent
- x: 0
- y: frame.y + frame.h
- w: @parent.w
- h: @parent.h - (frame.y + frame.h)
-
- @leftScreen.set
- parent: @parent
- x: 0
- y: frame.y
- w: frame.x
- h: frame.h
-
- @rightScreen.set
- parent: @parent
- x: frame.x + frame.w
- y: frame.y
- w: @parent.w - (frame.x + frame.w)
- h: frame.h
-
- @topScreen.draw ctx
- @leftScreen.draw ctx
- @rightScreen.draw ctx
- @bottomScreen.draw ctx
-
- drawHandles: (ctx) ->
- frame = @frame()
- frame.x = Math.round frame.x
- frame.y = Math.round frame.y
- frame.w = Math.round frame.w
- frame.h = Math.round frame.h
-
- newRect = (x, y) =>
- return new drawing.Rectangle
- parent: @
- x: x - (@handleSize / 2) - 0.5
- y: y - (@handleSize / 2) - 0.5
- w: @handleSize
- h: @handleSize
- lineWidth: 1
- strokeStyle: 'rgba(192, 192, 192, 1)'
- fillStyle: 'rgba(64, 64, 64, 1)'
-
- @handles["tl"] = newRect 0, 0
- @handles["tm"] = newRect (frame.w / 2), 0
- @handles["tr"] = newRect frame.w, 0
-
- @handles["ml"] = newRect 0, (frame.h / 2)
- @handles["mr"] = newRect frame.w, (frame.h / 2)
-
- @handles["bl"] = newRect 0, frame.h
- @handles["bm"] = newRect (frame.w / 2), frame.h
- @handles["br"] = newRect frame.w, frame.h
-
- for direction, handle of @handles
- handle.draw ctx
-
- drawCropLines: (ctx) ->
- frame = @frame()
- frame.x = Math.round frame.x
- frame.y = Math.round frame.y
- frame.w = Math.round frame.w
- frame.h = Math.round frame.h
-
- opacity = "0.5"
- lineDash = 8
-
- @isolateAndMoveToParent ctx, (ctx) =>
- ctx.beginPath()
- ctx.strokeStyle = "rgba(255,255,255,#{opacity})"
- ctx.rect 0.5, 0.5, frame.w, frame.h
- ctx.closePath()
- ctx.stroke()
-
- ctx.beginPath()
- ctx.strokeStyle = "rgba(0,0,0,#{opacity})"
- ctx.setLineDash [lineDash]
- ctx.rect 0.5, 0.5, frame.w, frame.h
- ctx.closePath()
- ctx.stroke()
-
- for x in [ frame.w / 3 + 0.5, (frame.w / 3) * 2 + 0.5 ]
- ctx.beginPath()
- ctx.moveTo x, 0
- ctx.strokeStyle = "rgba(255,255,255,#{opacity})"
- ctx.setLineDash []
- ctx.lineTo x, frame.h
- ctx.stroke()
-
- ctx.beginPath()
- ctx.moveTo x, 0
- ctx.strokeStyle = "rgba(0,0,0,#{opacity})"
- ctx.setLineDash [lineDash]
- ctx.lineTo x, frame.h
- ctx.stroke()
-
- for y in [ frame.h / 3 + 0.5, (frame.h / 3) * 2 + 0.5 ]
- ctx.beginPath()
- ctx.moveTo 0, y
- ctx.strokeStyle = "rgba(255,255,255,#{opacity})"
- ctx.setLineDash []
- ctx.lineTo frame.w, y
- ctx.stroke()
-
- ctx.beginPath()
- ctx.moveTo 0, y
- ctx.strokeStyle = "rgba(0,0,0,#{opacity})"
- ctx.setLineDash [lineDash]
- ctx.lineTo frame.w, y
- ctx.stroke()
-
- draw: (ctx) ->
- @drawScreen ctx
- @drawCropLines ctx
- @drawHandles ctx
-
class Stage extends drawing.Drawable
initialize: (options) ->
@canvas = options.canvas
Please sign in to comment.
Something went wrong with that request. Please try again.