unit uVScroll; interface uses SmartCL.System, System.Types, EventObjs, W3C.DOM, System.Colors, System.Diagnostics, SmartCL.Components; type TW3DispatchHandle = THandle; TW3DOMEvent = class(TEventObj); type TW3Range = record Maximum: integer; Minimum: integer; class function Create(const aMinimum: Integer; const aMaximum: Integer) : TW3Range; function ClipTo(const Value: Integer) : Integer; end; TScrollContent = class(TW3CustomControl) procedure StyleTagObject; override; empty; end; TW3ScrollIndicator = class(TW3MovableControl) procedure StyleTagObject; override; empty; end; TW3VScrollControl = class(TW3MovableControl) private FYOffset: integer; FContent: TScrollContent; FVRange: TW3Range; FHRange: TW3Range; FPressed: boolean; FStartY: integer; FTarget: integer; FAmplitude: double; FTimestamp: integer; FVelocity: double; FFrame: double; FTicker: TW3DispatchHandle; FFader: TW3DispatchHandle; FTimeConstant: double; FMouseDownEvent: TW3DOMEvent; FMouseUpEvent: TW3DOMEvent; FMouseMoveEvent: TW3DOMEvent; FTouchDownEvent: TW3DOMEvent; FTouchMoveEvent: TW3DOMEvent; FTouchEndsEvent: TW3DOMEvent; FIndicator: TW3ScrollIndicator; function GetYPosition(const E: variant): integer; procedure MoveBegins(sender: TObject; EventObj: JEvent); procedure MoveEnds(sender: TObject; EventObj: JEvent); procedure MoveUpdate(sender: TObject; EventObj: JEvent); protected procedure Track;virtual; procedure AutoScroll;virtual; procedure ScrollBegins;virtual; procedure ScrollEnds;virtual; procedure Resize;override; procedure StyleTagObject; override; empty; procedure InitializeObject; override; procedure FinalizeObject; override; procedure ObjectReady;override; procedure ScrollY(const NewTop: integer); public procedure HandleContentSizeChanged(sender: TObject); published property Angle: Float; property Zoom: Float; property BorderRadius: Integer; property Content:TScrollContent read FContent; end; implementation //################################################################### // TW3Range //################################################################### class function TW3Range.Create(const aMinimum: Integer; const aMaximum: Integer) : TW3Range; begin Result.Minimum := aMinimum; Result.Maximum := aMaximum; end; function TW3Range.ClipTo(const Value: Integer) : Integer; begin Result := if Value> Maximum then Maximum else if Value0 then begin Elapsed := ceil(PerformanceTimer.Now) - FTimestamp; Delta := -FAmplitude * Exp(-Elapsed / FTimeConstant); end; (* Scrolled passed end-of-document ? *) if (FYOffset >= (FContent.Height - ClientHeight)) then begin w3_ClearInterval(FTicker); FTicker := unassigned; ScrollY(FContent.Height-ClientHeight); ScrollEnds; exit; end; (* Scrolling breaches beginning of document? *) if (FYOffset < 0) then begin w3_ClearInterval(FTicker); FTicker := unassigned; ScrollY(0); ScrollEnds; exit; end; if (delta > 5) or (delta < -5) then begin ScrollY(FTarget + Delta); W3_RequestAnimationFrame(AutoScroll); end else begin ScrollY(FTarget); ScrollEnds; end; end; function TW3VScrollControl.GetYPosition(const e: variant): integer; begin if ( (e.targetTouches) and (e.targetTouches.length >0)) then result := e.targetTouches[0].clientY else result := e.clientY; end; procedure TW3VScrollControl.MoveBegins(sender: TObject; EventObj: JEvent); begin FPressed := true; FStartY := GetYPosition(EventObj); FVelocity := 0; FAmplitude := 0; FFrame := FYOffset; FTimestamp := ceil(PerformanceTimer.Now); w3_ClearInterval(FTicker); FTicker := w3_SetInterval(Track,100); EventObj.preventDefault(); EventObj.stopPropagation(); end; procedure TW3VScrollControl.MoveUpdate(sender: TObject; EventObj: JEvent); var y, delta: integer; begin if FPressed then begin y := GetYPosition(eventObj); delta := (FStartY - Y); if (Delta>2) or (Delta < -2) then begin FStartY := Y; ScrollY(FYOffset + Delta); end; end; EventObj.preventDefault(); EventObj.stopPropagation(); end; procedure TW3VScrollControl.MoveEnds(sender: TObject; EventObj: JEvent); begin FPressed := false; w3_ClearInterval(FTicker); if (FVelocity > 10) or (FVelocity < -10) then begin FAmplitude := 0.8 * FVelocity; FTarget := round(FYOffset + FAmplitude); FTimeStamp := ceil(PerformanceTimer.Now); ScrollBegins; w3_requestAnimationFrame(autoscroll); end; EventObj.preventDefault(); EventObj.stopPropagation(); end; end.