mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Moved DOM highlight arrows from PlayLevelView to CocoView so that everywhere can use them. Added highlight for next level on world map.
This commit is contained in:
parent
c6b398aae2
commit
589c3b090c
6 changed files with 90 additions and 83 deletions
|
@ -25,7 +25,7 @@ module.exports = class PlaybackOverScreen extends CocoClass
|
|||
show: ->
|
||||
return if @showing
|
||||
@showing = true
|
||||
@updateColor 'rgba(212, 212, 212, 0.4' unless @color # If we haven't caught the goal state for the first run, just do something neutral.
|
||||
@updateColor 'rgba(212, 212, 212, 0.4)' unless @color # If we haven't caught the goal state for the first run, just do something neutral.
|
||||
@dimLayer.alpha = 0
|
||||
createjs.Tween.removeTweens @dimLayer
|
||||
createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
|
||||
|
|
|
@ -236,6 +236,17 @@ table.table
|
|||
.ui-state-default, .ui-state-focus, .ui-state-active, .ui-state-highlight, .ui-state-error
|
||||
background-image: none
|
||||
|
||||
// DOM highlight pointer arrow
|
||||
|
||||
.highlight-pointer
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
height: 100px
|
||||
opacity: 0.0
|
||||
pointer-events: none
|
||||
z-index: 10
|
||||
|
||||
// Fonts
|
||||
|
||||
.header-font
|
||||
|
|
|
@ -97,15 +97,6 @@ $level-resize-transition-time: 0.5s
|
|||
bottom: 0
|
||||
@include transition(width $level-resize-transition-time ease-in-out, right $level-resize-transition-time ease-in-out)
|
||||
|
||||
#pointer
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
height: 100px
|
||||
opacity: 0.0
|
||||
pointer-events: none
|
||||
z-index: 10
|
||||
|
||||
// Level Docs
|
||||
.ui-effects-transfer
|
||||
border: 2px dotted gray
|
||||
|
|
|
@ -59,6 +59,7 @@ module.exports = class CocoView extends Backbone.View
|
|||
@undelegateEvents() # removes both events and subs
|
||||
view.destroy() for id, view of @subviews
|
||||
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
|
||||
@endHighlight()
|
||||
@[key] = undefined for key, value of @
|
||||
@destroyed = true
|
||||
@off = doNothing
|
||||
|
@ -293,6 +294,78 @@ module.exports = class CocoView extends Backbone.View
|
|||
delete @subviews[view.parentKey]
|
||||
view.destroy()
|
||||
|
||||
# Pointing stuff out
|
||||
|
||||
highlightElement: (selector, options) ->
|
||||
@endHighlight()
|
||||
options ?= {}
|
||||
if delay = options.delay
|
||||
delete options.delay
|
||||
return @pointerDelayTimeout = _.delay((=> @highlightElement selector, options), delay)
|
||||
$pointer = @getPointer()
|
||||
$target = $(selector + ':visible')
|
||||
return if parseFloat($target.css('opacity')) is 0.0 # Don't point out invisible elements.
|
||||
return unless offset = $target.offset() # Don't point out elements we can't locate.
|
||||
targetLeft = offset.left + $target.outerWidth() * 0.5
|
||||
targetTop = offset.top + $target.outerHeight() * 0.5
|
||||
|
||||
if options.sides
|
||||
if 'left' in options.sides then targetLeft = offset.left
|
||||
if 'right' in options.sides then targetLeft = offset.left + $target.outerWidth()
|
||||
if 'top' in options.sides then targetTop = offset.top
|
||||
if 'bottom' in options.sides then targetTop = offset.top + $target.outerHeight()
|
||||
else
|
||||
# Aim to hit the side if the target is entirely on one side of the screen.
|
||||
if offset.left > @$el.outerWidth() * 0.5
|
||||
targetLeft = offset.left
|
||||
else if offset.left + $target.outerWidth() < @$el.outerWidth() * 0.5
|
||||
targetLeft = offset.left + $target.outerWidth()
|
||||
|
||||
# Aim to hit the bottom or top if the target is entirely on the top or bottom of the screen.
|
||||
if offset.top > @$el.outerWidth() * 0.5
|
||||
targetTop = offset.top
|
||||
else if offset.top + $target.outerHeight() < @$el.outerHeight() * 0.5
|
||||
targetTop = offset.top + $target.outerHeight()
|
||||
|
||||
if options.offset
|
||||
targetLeft += options.offset.x
|
||||
targetTop += options.offset.y
|
||||
|
||||
@pointerRadialDistance = -47
|
||||
@pointerRotation = options.rotation ? Math.atan2(@$el.outerWidth() * 0.5 - targetLeft, targetTop - @$el.outerHeight() * 0.5)
|
||||
$pointer.css
|
||||
opacity: 1.0
|
||||
transition: 'none'
|
||||
transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)"
|
||||
top: targetTop - 50
|
||||
left: targetLeft - 50
|
||||
_.defer =>
|
||||
return if @destroyed
|
||||
@animatePointer()
|
||||
clearInterval @pointerInterval
|
||||
@pointerInterval = setInterval(@animatePointer, 1200)
|
||||
if options.duration
|
||||
@pointerDurationTimeout = _.delay (=> @endHighlight() unless @destroyed), options.duration
|
||||
|
||||
animatePointer: =>
|
||||
$pointer = @getPointer()
|
||||
$pointer.css transition: 'all 0.6s ease-out', transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)"
|
||||
#Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'dom_highlight', volume: 0.75 # Never mind, this is currently so annoying
|
||||
setTimeout (=> $pointer.css transition: 'all 0.4s ease-in', transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)"), 800
|
||||
|
||||
endHighlight: ->
|
||||
@getPointer().css('opacity', 0.0)
|
||||
clearInterval @pointerInterval
|
||||
clearTimeout @pointerDelayTimeout
|
||||
clearTimeout @pointerDurationTimeout
|
||||
@pointerInterval = @pointerDelayTimeout = @pointerDurationTimeout = null
|
||||
|
||||
getPointer: ->
|
||||
return $pointer if ($pointer = @$el.find('.highlight-pointer')) and $pointer.length
|
||||
$pointer = $('<img src="/images/level/pointer.png" class="highlight-pointer">')
|
||||
@$el.append($pointer)
|
||||
$pointer
|
||||
|
||||
# Utilities
|
||||
|
||||
getQueryVariable: (param, defaultValue) -> CocoView.getQueryVariable(param, defaultValue)
|
||||
|
|
|
@ -89,6 +89,7 @@ module.exports = class WorldMapView extends RootView
|
|||
@$el.find('.level').tooltip()
|
||||
@$el.addClass _.string.slugify @terrain
|
||||
@updateVolume()
|
||||
@highlightElement '.level.next', delay: 8000, duration: 20000, rotation: 0, sides: ['top']
|
||||
|
||||
onSessionsLoaded: (e) ->
|
||||
for session in @sessions.models
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
'level:set-volume': (e) -> createjs.Sound.setVolume(if e.volume is 1 then 0.6 else e.volume) # Quieter for now until individual sound FX controls work again.
|
||||
'level:show-victory': 'onShowVictory'
|
||||
'level:restart': 'onRestartLevel'
|
||||
'level:highlight-dom': 'onHighlightDom'
|
||||
'level:highlight-dom': 'onHighlightDOM'
|
||||
'level:end-highlight-dom': 'onEndHighlight'
|
||||
'level:focus-dom': 'onFocusDom'
|
||||
'level:disable-controls': 'onDisableControls'
|
||||
|
@ -430,9 +430,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
viewArgs: [{supermodel: @supermodel, autoUnveil: true}, @levelID]
|
||||
}
|
||||
|
||||
onWindowResize: (s...) ->
|
||||
$('#pointer').css('opacity', 0.0)
|
||||
clearInterval(@pointerInterval)
|
||||
onWindowResize: (e) -> @endHighlight()
|
||||
|
||||
onDisableControls: (e) ->
|
||||
return if e.controls and not ('level' in e.controls)
|
||||
|
@ -500,73 +498,12 @@ module.exports = class PlayLevelView extends RootView
|
|||
return null unless @getNextLevelID()
|
||||
"/play/level/#{@getNextLevelID()}"
|
||||
|
||||
onHighlightDom: (e) ->
|
||||
if e.delay
|
||||
delay = e.delay
|
||||
delete e.delay
|
||||
@pointerInterval = _.delay((=> @onHighlightDom e), delay)
|
||||
return
|
||||
@addPointer()
|
||||
selector = e.selector + ':visible'
|
||||
dom = $(selector)
|
||||
return if parseFloat(dom.css('opacity')) is 0.0
|
||||
offset = dom.offset()
|
||||
return if not offset
|
||||
target_left = offset.left + dom.outerWidth() * 0.5
|
||||
target_top = offset.top + dom.outerHeight() * 0.5
|
||||
onHighlightDOM: (e) -> @highlightElement e.selector, delay: e.delay, sides: e.sides, offset: e.offset, rotation: e.rotation
|
||||
|
||||
if e.sides
|
||||
if 'left' in e.sides then target_left = offset.left
|
||||
if 'right' in e.sides then target_left = offset.left + dom.outerWidth()
|
||||
if 'top' in e.sides then target_top = offset.top
|
||||
if 'bottom' in e.sides then target_top = offset.top + dom.outerHeight()
|
||||
else
|
||||
# aim to hit the side if the target is entirely on one side of the screen
|
||||
if offset.left > @$el.outerWidth()*0.5
|
||||
target_left = offset.left
|
||||
else if offset.left + dom.outerWidth() < @$el.outerWidth()*0.5
|
||||
target_left = offset.left + dom.outerWidth()
|
||||
|
||||
# aim to hit the bottom or top if the target is entirely on the top or bottom of the screen
|
||||
if offset.top > @$el.outerWidth()*0.5
|
||||
target_top = offset.top
|
||||
else if offset.top + dom.outerHeight() < @$el.outerHeight()*0.5
|
||||
target_top = offset.top + dom.outerHeight()
|
||||
|
||||
if e.offset
|
||||
target_left += e.offset.x
|
||||
target_top += e.offset.y
|
||||
|
||||
@pointerRadialDistance = -47 # - Math.sqrt(Math.pow(dom.outerHeight()*0.5, 2), Math.pow(dom.outerWidth()*0.5))
|
||||
@pointerRotation = e.rotation ? Math.atan2(@$el.outerWidth()*0.5 - target_left, target_top - @$el.outerHeight()*0.5)
|
||||
pointer = $('#pointer')
|
||||
pointer
|
||||
.css('opacity', 1.0)
|
||||
.css('transition', 'none')
|
||||
.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)")
|
||||
.css('top', target_top - 50)
|
||||
.css('left', target_left - 50)
|
||||
setTimeout(()=>
|
||||
return if @destroyed
|
||||
@animatePointer()
|
||||
clearInterval(@pointerInterval)
|
||||
@pointerInterval = setInterval(@animatePointer, 1200)
|
||||
, 1)
|
||||
|
||||
animatePointer: =>
|
||||
pointer = $('#pointer')
|
||||
pointer.css('transition', 'all 0.6s ease-out')
|
||||
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)")
|
||||
#Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'dom_highlight', volume: 0.75 # Never mind, this is currently so annoying
|
||||
setTimeout((=>
|
||||
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800)
|
||||
onEndHighlight: -> @endHighlight()
|
||||
|
||||
onFocusDom: (e) -> $(e.selector).focus()
|
||||
|
||||
onEndHighlight: ->
|
||||
$('#pointer').css('opacity', 0.0)
|
||||
clearInterval(@pointerInterval)
|
||||
|
||||
onMultiplayerChanged: (e) ->
|
||||
if @session.get('multiplayer')
|
||||
@bus.connect()
|
||||
|
@ -574,11 +511,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
@bus.removeFirebaseData =>
|
||||
@bus.disconnect()
|
||||
|
||||
addPointer: ->
|
||||
p = $('#pointer')
|
||||
return if p.length
|
||||
@$el.append($('<img src="/images/level/pointer.png" id="pointer">'))
|
||||
|
||||
preloadNextLevel: =>
|
||||
# TODO: Loading models in the middle of gameplay causes stuttering. Most of the improvement in loading time is simply from passing the supermodel from this level to the next, but if we can find a way to populate the level early without it being noticeable, that would be even better.
|
||||
# return if @destroyed
|
||||
|
@ -652,7 +584,6 @@ module.exports = class PlayLevelView extends RootView
|
|||
createjs.Tween.get(ambientSound).to({volume: 0.0}, 1500).call -> ambientSound.stop()
|
||||
$(window).off 'resize', @onWindowResize
|
||||
delete window.world # not sure where this is set, but this is one way to clean it up
|
||||
clearInterval(@pointerInterval)
|
||||
@bus?.destroy()
|
||||
#@instance.save() unless @instance.loading
|
||||
delete window.nextLevelURL
|
||||
|
|
Loading…
Reference in a new issue