This commit is contained in:
세젤귀 고양이들 2022-01-06 23:26:13 +09:00
parent b59a3b2586
commit 79a603dfcb

View file

@ -1,38 +1,42 @@
local Quad = require(game.ReplicatedStorage:WaitForChild "Quad"); ---@module Quad.src local Quad = require(game.ReplicatedStorage:WaitForChild "Quad"); ---@module "Quad.src"
local _ = Quad.init "IosUI"; local round,class,mount,store,event,tween = _.round,_.class,_.mount,_.store,_.event,_.tween; local _ = Quad.init "components"; local round,class,mount,store,event,tween = _.round,_.class,_.mount,_.store,_.event,_.tween;
-- init quad
local scrollFrame = class.extend();
local connection = event.new;
-- const
local white = Color3.fromRGB(255,255,255);
local scrollMut = 100;
local elasticMut = 20;
local fitTime = 0.22;
local tweenTime = 0.63;
local mouseUpMut = 42;
local max = math.max; local max = math.max;
local min = math.min; local min = math.min;
local abs = math.abs; local abs = math.abs;
-- base objects -- init quad
local scrollFrame = class.extend();
local connection = event.new;
local frame = class "Frame"; local frame = class "Frame";
frame.BorderSizePixel = 0; frame.BorderSizePixel = 0;
local button = class "TextButton"; local button = class "TextButton";
button.BorderSizePixel = 0; button.BorderSizePixel = 0;
button.Text = nil; button.Text = "";
button.AutoButtonColor = false;
local image = class "ImageLabel"; local image = class "ImageLabel";
image.BackgroundTransparency = 1;
-- const
local white = Color3.fromRGB(255,255,255);
local scrollMut = 100; -- Used for multiply input when using mosue scroll
local elasticMut = 20; -- Used for multiply input when overscrolling
local fitTime = 0.22; -- Used for reset overscroll when mouse scroll
local tweenTime = 0.63; -- Used for tween time
local mouseUpMut = 18; -- Used for multiply input when drag input ended
local dragRestMut = 5; -- Used for multiply input when reset overscroll from drag input
local dragOverMut = 0.3; -- Used for multiply input when overscrolling from drag input
local scrollbarOverMut = 0.18; -- Used for multiply size of scrollbar when overscrolling
-- tween for moving -- tween for moving
local out = tween.EasingDirections.Out; local out = tween.EasingDirections.Out;
local exp2 = tween.EasingFunctions.Exp2; local exp2 = tween.EasingFunctions.Exp2;
local tweenData = { local tweenData = {
Time = tweenTime; Time = tweenTime;
Direction = out; Direction = out;
EasingFunction = exp2; EasingFunction = exp2;
}; };
local runTween = tween.RunTween; local runTween = tween.RunTween;
local stopTween = tween.StopTween;
-- user input service -- user input service
local userInputService = game:GetService "UserInputService"; local userInputService = game:GetService "UserInputService";
@ -46,197 +50,343 @@ local touch = Enum.UserInputType.Touch;
-- Calc Elastic -- Calc Elastic
local calcElastic do local calcElastic do
local maxLogX = 32; local maxLogX = 30;
local mlog = math.log; local mlog = math.log;
local maxlog = mlog(maxLogX+1,2); local maxlog = mlog(maxLogX+1,2);
local function log(x) local function log(x)
return min(mlog(x*maxLogX+1,2),maxlog)/maxlog; return min(mlog(x*maxLogX+1,2),maxlog)/maxlog;
end end
function calcElastic (elastic,pos,max) function calcElastic (elastic,pos,max)
if pos < 0 then if pos < 0 then
return - (log(abs(pos)/scrollMut*elasticMut/elastic)*elastic); return - (log(abs(pos)/scrollMut*elasticMut/elastic)*elastic);
elseif pos > max then elseif pos > max then
return max + (log((pos-max)/scrollMut*elasticMut/elastic)*elastic); return max + (log((pos-max)/scrollMut*elasticMut/elastic)*elastic);
end end
return pos; return pos;
end end
end end
-- Init class -- Init class
function scrollFrame:init(props) function scrollFrame:init(props)
local CanvasPosition = props.CanvasPosition or Vector2.new(0,0); local CanvasPosition = props.CanvasPosition or Vector2.new(0,0);
self._targetX = CanvasPosition.X; self._targetX = CanvasPosition.X;
self._targetY = CanvasPosition.Y; self._targetY = CanvasPosition.Y;
props.CanvasPosition = CanvasPosition; props.CanvasPosition = CanvasPosition;
self._inputIndex = -20000000; self._inputIndex = -20000000;
self._mouseConnection = connection(); self._mouseConnection = connection();
props:default("CanvasPosition",CanvasPosition); self._overscrollX = false;
props:default("CanvasSize",UDim2.fromScale(1,2)); self._overscrollY = false;
props:default("Size",UDim2.new(1,0,1,0)); props:default("CanvasPosition",CanvasPosition);
props:default("Position",UDim2.new(0,0,0,0)); props:default("CanvasSize",UDim2.fromScale(1,2));
props:default("Elastic",122); props:default("Size",UDim2.new(1,0,1,0));
props:default("OverscrollX",false); props:default("Position",UDim2.new(0,0,0,0));
props:default("OverscrollY",true); props:default("Elastic",142);
props:default("BackgroundTransparency",1); props:default("OverscrollX",false);
props:default("ZIndex",1); props:default("OverscrollY",true);
props:default("BackgroundColor3",white); props:default("BackgroundTransparency",0);
props:default("ScrollbarSizeX",2); props:default("ZIndex",1);
props:default("ScrollbarVisibleY",false); props:default("BackgroundColor3",white);
props:default("ScrollbarSizeX",2); props:default("ScrollbarSizeX",2);
props:default("ScrollbarVisibleX",true); props:default("ScrollbarPositionY","Right");
props:default("ScrollbarPadding",2); props:default("ScrollbarPositionX","Bottom");
props:default("ScrollbarColor3",Color3.fromRGB(172, 172, 172)); props:default("ScrollbarVisibleY",true);
props:default("ScrollbarTransparency",0.5); props:default("ScrollbarSizeY",2);
props:default("ScrollbarVisibleX",true);
props:default("ScrollbarPadding",2);
props:default("ScrollbarColor3",Color3.fromRGB(145, 145, 145));
props:default("ScrollbarTransparency",0.3);
-- props "ScrollbarPadding":register(function ()
-- self:fit();
-- self.__prop
-- end);
-- props "ScrollbarPadding":register(function ()
-- self:fit();
-- self.__prop
-- end);
-- ScrollbarSizeX
end end
-- Render objects -- Render objects
function scrollFrame:render(props) function scrollFrame:render(props)
return button { return button {
Size = props "Size"; Size = props "Size";
Position = props "Position"; Position = props "Position";
BackgroundTransparency = props "BackgroundTransparency"; BackgroundTransparency = props "BackgroundTransparency";
BackgroundColor3 = props "BackgroundColor3"; BackgroundColor3 = props "BackgroundColor3";
ZIndex = props "ZIndex"; ZIndex = props "ZIndex";
ClipsDescendants = true; ClipsDescendants = true;
[event.createdSync] = function (this) [event.createdSync] = function (this)
this.InputChanged:Connect(function (input) this.InputChanged:Connect(function (input)
if self._mouseDown or self._scrollbarDown then if self._mouseDown or self._scrollbarDown then
return; return;
end end
local inputType = input.UserInputType; local inputType = input.UserInputType;
if inputType == mouseWheel then if inputType == mouseWheel then
local x,y = 0,1; local x,y = 0,1;
if input:IsModifierKeyDown(shiftModifier) then if input:IsModifierKeyDown(shiftModifier) then
x,y = 1,0; x,y = 1,0;
end end
local scroll = input.Position.Z * scrollMut; local scroll = input.Position.Z * scrollMut;
local inputIndex = self:update(self._targetX + x*scroll,self._targetY - y*scroll); local inputIndex = self:update(self._targetX + x*scroll,self._targetY - y*scroll);
delay(fitTime,function () delay(fitTime,function ()
if inputIndex == self._inputIndex then if inputIndex == self._inputIndex then
self:fit(); self:fit();
end end
end); end);
end end
end); end);
end; end;
[event "MouseButton1Down"] = function (this,downX,downY) [event "MouseButton1Down"] = function (this,downX,downY)
self._mouseDown = true; self._mouseDown = true;
local mouseConnection = self._mouseConnection; local mouseConnection = self._mouseConnection;
mouseConnection:disconnect(); mouseConnection:disconnect();
mouseConnection:add(inputEnded:Connect(function (input) mouseConnection:add(inputEnded:Connect(function (input)
self._mouseDown = false; self._mouseDown = false;
local inputType = input.UserInputType; local inputType = input.UserInputType;
if inputType == mouseButton1 or inputType == touch then if inputType == mouseButton1 or inputType == touch then
local position = input.Position; local position = input.Position;
local x = position.X; local x = position.X;
local y = position.Y; local y = position.Y;
self:update(self._targetX + ((x - downX) * mouseUpMut),self._targetY + ((- y + downY)*mouseUpMut)); self:update(self._targetX + ((x - downX) * mouseUpMut),self._targetY + ((- y + downY)*mouseUpMut));
mouseConnection:disconnect(); mouseConnection:disconnect();
self:fit(); self:fit();
end end
end)); end));
mouseConnection:add(inputChanged:Connect(function (input) mouseConnection:add(inputChanged:Connect(function (input)
if input.UserInputType == mouseMovement then if input.UserInputType == mouseMovement then
local position = input.Position; local position = input.Position;
local x = position.X; local x,y = position.X,position.Y;
local y = position.Y; local overscrollX,overscrollY = self._overscrollX,self._overscrollY;
local inputIndex = self:update(self._targetX + x - downX,self._targetY - y + downY);
delay(fitTime,function () local xIns = downX - x;
if inputIndex == self._inputIndex then if overscrollX and ((overscrollX == -1 and xIns > 0) or (overscrollX == 1 and xIns < 0)) then
self:fit(); xIns = xIns * dragRestMut;
end elseif overscrollX and ((overscrollX == -1 and xIns < 0) or (overscrollX == 1 and xIns > 0)) then
end); xIns = xIns * dragOverMut;
downX,downY = x,y; end
end
end)); local yIns = downY - y;
end; if overscrollY and ((overscrollY == -1 and yIns > 0) or (overscrollY == 1 and yIns < 0)) then
frame { yIns = yIns * dragRestMut;
[event.prop "AbsoluteSize"] = function () elseif overscrollY and ((overscrollY == -1 and yIns < 0) or (overscrollY == 1 and yIns > 0)) then
self:fit(); yIns = yIns * dragOverMut;
end; end
BackgroundTransparency = 1;
Size = props "CanvasSize"; self:update(self._targetX + xIns,self._targetY + yIns);
ZIndex = props "ZIndex"; downX,downY = x,y;
Position = UDim2.fromOffset(self._targetX,self._targetY); end
[event.createdSync] = function (this) end));
self._holder = this; end;
end; frame {
}; [event.prop "AbsoluteSize"] = function ()
button { self:fit();
Visible = props "ScrollbarVisibleY"; end;
Size = props "ScrollbarPadding,ScrollbarSizeX":with(function (store) BackgroundTransparency = 1;
self:fit(); Size = props "CanvasSize";
end); ZIndex = props "ZIndex";
image { Position = UDim2.fromOffset(self._targetX,self._targetY);
roundSize = 2000; [event.createdSync] = function (this)
ImageColor3 = props "ScrollbarColor3"; self._holder = this;
ImageTransparency = props "ScrollbarTransparency"; end;
Size = props "ScrollbarSizeX,ScrollbarPadding":with(function (store) };
return UDim2.new(0,store.ScrollbarSizeX,1,store.ScrollbarPadding * 2); button {
end); AnchorPoint = props "ScrollbarPositionY":with(function (_,value)
Position = UDim2.fromScale(0.5,0.5); return value == "Right" and Vector2.new(1,0) or Vector2.new(0,0);
AnchorPoint = Vector2.new(0.5,0.5); end);
}; [event.createdSync] = function (this)
} self._scrollbarY = this;
}; end;
Position = props.ScrollbarPositionY == "Right" and UDim2.new(1,0,0,0) or UDim2.new(0,0,0,0);
BackgroundTransparency = 1;
Visible = props "ScrollbarVisibleY";
image {
roundSize = 2000;
ImageColor3 = props "ScrollbarColor3";
ImageTransparency = props "ScrollbarTransparency";
Size = props "ScrollbarSizeY,ScrollbarPadding":with(function (store)
return UDim2.new(0,store.ScrollbarSizeY,1,store.ScrollbarPadding * -2);
end);
Position = UDim2.fromScale(0.5,0.5);
AnchorPoint = Vector2.new(0.5,0.5);
};
};
button {
AnchorPoint = props "ScrollbarPositionX":with(function (_,value)
return value == "Top" and Vector2.new(0,0) or Vector2.new(0,1);
end);
[event.createdSync] = function (this)
self._scrollbarX = this;
end;
Position = props.ScrollbarPositionX == "Top" and UDim2.new(0,0,0,0) or UDim2.new(0,0,1,0);
BackgroundTransparency = 1;
Visible = props "ScrollbarVisibleX";
image {
roundSize = 2000;
ImageColor3 = props "ScrollbarColor3";
ImageTransparency = props "ScrollbarTransparency";
Size = props "ScrollbarSizeX,ScrollbarPadding":with(function (store)
return UDim2.new(1,store.ScrollbarPadding * -2,0,store.ScrollbarSizeX);
end);
Position = UDim2.fromScale(0.5,0.5);
AnchorPoint = Vector2.new(0.5,0.5);
};
};
};
end end
-- Set scroll position with allowing overflow -- Set scroll position with allowing overflow
function scrollFrame:update(x,y) function scrollFrame:update(x,y)
x,y = x or self._targetX,y or self._targetY; x,y = x or self._targetX,y or self._targetY;
local inputIndex = self._inputIndex + 1; local inputIndex = self._inputIndex + 1;
self._inputIndex = inputIndex; self._inputIndex = inputIndex;
local holder = self._holder; local holder = self._holder;
local holderAbsSize = holder.AbsoluteSize; local holderAbsSize = holder.AbsoluteSize;
local objectAbsSize = self.__object.AbsoluteSize; local objectAbsSize = self.__object.AbsoluteSize;
local maxX = holderAbsSize.X - objectAbsSize.X; local holderX,holderY = holderAbsSize.X,holderAbsSize.Y;
local maxY = holderAbsSize.Y - objectAbsSize.Y; local objectX,objectY = objectAbsSize.X,objectAbsSize.Y;
local maxX = holderX - objectX;
local maxY = holderY - objectY;
local elastic = self.Elastic; local elastic = self.Elastic;
x = self.OverscrollX and calcElastic(elastic,x,maxX) or max(min(maxX,x),0); x = self.OverscrollX and calcElastic(elastic,x,maxX) or max(min(maxX,x),0);
y = self.OverscrollY and calcElastic(elastic,y,maxY) or max(min(maxY,y),0); y = self.OverscrollY and calcElastic(elastic,y,maxY) or max(min(maxY,y),0);
runTween(self._holder,tweenData,{ local overflowX = (x<0 and -1) or (x>maxX and 1);
Position = UDim2.fromOffset(x,-y); local overflowY = (y<0 and -1) or (y>maxY and 1);
}); self._overscrollX = overflowX;
self._targetX,self._targetY = x,y; self._overscrollY = overflowY;
self:updateScrollbar(x,y);
return inputIndex; runTween(holder,tweenData,{
Position = UDim2.fromOffset(-x,-y);
});
self._targetX,self._targetY = x,y;
self:updateScrollbar(
x,y,objectX,objectY,holderX,holderY,elastic,
(overflowX == -1 and abs(x)) or (overflowX == 1 and x-maxX),
(overflowY == -1 and abs(y)) or (overflowY == 1 and y-maxY)
);
return inputIndex;
end end
-- Get canvas position -- Get canvas position
function scrollFrame.getter:CanvasPosition() function scrollFrame.getter:CanvasPosition()
return Vector2.new(self._targetX,self._targetY); return Vector2.new(self._targetX,self._targetY);
end end
-- Set canvas postion -- Set canvas postion
function scrollFrame.setter:CanvasPosition(pos) function scrollFrame.setter:CanvasPosition(pos)
local inputIndex = self:update(pos.X,pos.Y); local inputIndex = self:update(pos.X,pos.Y);
delay(fitTime,function () delay(fitTime,function ()
if inputIndex == self._inputIndex then if inputIndex == self._inputIndex then
self:fit(); self:fit();
end end
end); end);
end end
-- Fit position (remove overflow) -- Fit position (remove overflow)
function scrollFrame:fit() function scrollFrame:fit()
local targetY = self._targetY; local targetY = self._targetY;
local targetX = self._targetX; local targetX = self._targetX;
local holder = self._holder; local holder = self._holder;
local holderAbsoluteSize = holder.AbsoluteSize; local holderAbsoluteSize = holder.AbsoluteSize;
local objectAbsSize = self.__object.AbsoluteSize; local objectAbsSize = self.__object.AbsoluteSize;
local maxX = holderAbsoluteSize.X - objectAbsSize.X; local maxX = holderAbsoluteSize.X - objectAbsSize.X;
local maxY = holderAbsoluteSize.Y - objectAbsSize.Y; local maxY = holderAbsoluteSize.Y - objectAbsSize.Y;
if targetX<0 or targetY<0 or targetX>maxX or targetY>maxY then if targetX<0 or targetY<0 or targetX>maxX or targetY>maxY then
self:update( self:update(
max(min(targetX,maxX),0), max(min(targetX,maxX),0),
max(min(targetY,maxY),0) max(min(targetY,maxY),0)
); );
else else
self:updateScrollbar(targetX,targetY); self:updateScrollbar(targetX,targetY);
end end
end
-- update scrollbar
function scrollFrame:updateScrollbar(x,y,objectX,objectY,holderX,holderY,elastic,overflowX,overflowY)
local scrollbarY = self._scrollbarY;
local scrollbarX = self._scrollbarX;
local objectSize;
if not objectY then
objectSize = self.__object.AbsoluteSize;
objectY = objectSize.Y;
end
if not objectX then
if not objectSize then
objectSize = self.__object.AbsoluteSize;
end
objectX = objectSize.X;
end
local holderSize;
if not holderY then
holderSize = self._holder.AbsoluteSize;
holderY = holderSize.Y;
end
if not holderX then
if not holderSize then
holderSize = self._holder.AbsoluteSize;
end
holderX = holderSize.X;
end
elastic = elastic or self.Elastic;
if scrollbarY then
local size;
if objectY >= holderY then
size = 0;
else
size = objectY / holderY;
if overflowY then
size = size * (1-overflowY/elastic) * scrollbarOverMut;
end
end
local sizeUdim = UDim2.new(0,self.ScrollbarSizeY+(self.ScrollbarPadding*2),size,0);
local pos = 0;
if size ~= 0 then
pos = (1-size)*(min(max(y/(holderY-objectY),0),1));
end
local posUdim = UDim2.new(self.ScrollbarPositionY == "Right" and 1 or 0,0,pos,0);
if self._mouseDown then
stopTween(scrollbarY);
scrollbarY.Position = posUdim;
scrollbarY.Size = sizeUdim;
else
runTween(scrollbarY,tweenData,{
Position = posUdim;
Size = sizeUdim;
});
end
end
if scrollbarX then
local size;
if objectX >= holderX then
size = 0;
else
size = objectX / holderX;
if overflowX then
size = size * (1-overflowX/elastic) * scrollbarOverMut;
end
end
local sizeUdim = UDim2.new(size,0,0,self.ScrollbarSizeX+(self.ScrollbarPadding*2));
local pos = 0;
if size ~= 0 then
pos = (1-size)*(min(max(x/(holderX-objectX),0),1));
end
local posUdim = UDim2.new(pos,0,self.ScrollbarPositionX == "Top" and 0 or 1,0);
if self._mouseDown then
stopTween(scrollbarX);
scrollbarX.Position = posUdim;
scrollbarX.Size = sizeUdim;
else
runTween(scrollbarX,tweenData,{
Position = posUdim;
Size = sizeUdim;
});
end
end
end end
return scrollFrame; return scrollFrame;