Skip to content

Instantly share code, notes, and snippets.

@skinnyjames
Created June 3, 2026 09:57
Show Gist options
  • Select an option

  • Save skinnyjames/4f0628a5a87fd5f48960a98be648bb82 to your computer and use it in GitHub Desktop.

Select an option

Save skinnyjames/4f0628a5a87fd5f48960a98be648bb82 to your computer and use it in GitHub Desktop.
friction.rb
module HackOS
class Panel < Hokusai::Block
template <<~EOF
[template]
clipped {
@tap="on_tapdown"
@drag="on_taphold"
@taphold="on_taphold"
@swipe="on_swipe"
@taprelease="on_taprelease"
:autoclip="autoclip"
:offset="offset"
}
dynamic { @size_updated="set_size" }
slot
EOF
uses(
clipped: Hokusai::Blocks::Clipped,
dynamic: Hokusai::Blocks::Dynamic,
)
computed :autoclip, default: true
computed :friction, default: 0.96, convert: proc(&:to_f)
attr_accessor :top, :offset, :clipped_content_height, :panel_height
def on_tapdown(event)
@tap = event.pos.y
@timer ||= Timer.new # for swipes
@velocity = nil
end
def on_taprelease(event)
@tap = nil
@delta = nil
@timer = nil
end
# move the screen according to hold pos.y
def on_taphold(event)
@delta = (@tap - event.pos.y.to_f)
@tap = event.pos.y
@timer.restart
clamp_offset(@delta)
end
def on_swipe(event)
if event.swipe_direction == :up || event.swipe_direction == :down
@velocity = (@delta / (@timer.elapsed * 1000))
end
end
def clamp_offset(delta)
if offset + delta < panel_top
self.offset = panel_top
elsif offset + delta > content_height - panel_height
self.offset = content_height - panel_height
else
self.offset += delta
end
end
def momentum
@velocity *= friction;
if @velocity.abs < 0.01
@velocity = nil
return
end
offsetdelta = @velocity * 16
clamp_offset(offsetdelta)
end
def set_size(_, height)
if panel_height != clipped_content_height || clipped_content_height.zero?
self.clipped_content_height = height
end
end
def content_height
clipped_content_height
end
def panel_top
top || 0.0
end
def set_size(_, height)
if panel_height != clipped_content_height || clipped_content_height.zero?
self.clipped_content_height = height
end
end
def initialize(**args)
@top = nil
@offset = 0
@clipped_content_height = 0
@panel_height = 0
@velocity = 0.0 # between 0 and 1
super
end
def render(canvas)
self.top ||= canvas.y
self.panel_height = canvas.height
if @velocity
momentum
end
@timer&.next
yield canvas
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment