2012-05-02 21 views
5

मैं एक साधारण एक TScrollingWinControl (और एक TScrollBox से नकल कोड) एक TImage नियंत्रण के साथ के आधार पर नियंत्रण बना रहा हूं। मुझे कुछ हद तक काम करने के लिए ज़ूमिंग मिल गई, लेकिन यह जरूरी नहीं कि एक केंद्रित बिंदु पर ज़ूम करें - स्क्रॉलबार केंद्र बिंदु को ध्यान में रखने के लिए तदनुसार नहीं बदलते हैं।किसी विशेष फोकस के लिए एक TScrollBox के अंदर एक TImage में ज़ूम इन/आउट करना?

मैं कहाँ करने के लिए ध्यान केंद्रित ज़ूम करने के लिए यह बताने के लिए इस पर नियंत्रण ZoomTo(const X, Y, ZoomBy: Integer); बताने के लिए सक्षम होने के लिए करना चाहते हैं। तो जब यह ज़ूम करता है, तो मेरे द्वारा पारित निर्देशांक 'केंद्रित' रहेंगे। साथ ही, मुझे ZoomBy(const ZoomBy: Integer); भी होना चाहिए जो इसे वर्तमान दृश्य में केंद्रित रखने के लिए कहता है।

उदाहरण के लिए, एक परिदृश्य होगा जहां माउस छवि के किसी विशेष बिंदु पर इंगित किया गया है, और माउस पकड़ने पर नियंत्रण और माउस स्क्रॉल करते समय, इसे माउस पॉइंटर पर ध्यान केंद्रित करना चाहिए। दूसरी तरफ, एक और परिदृश्य ज़ूम स्तर को समायोजित करने के लिए नियंत्रण को स्लाइड करेगा, इस स्थिति में इसे केवल वर्तमान दृश्य (आवश्यक रूप से छवि का केंद्र नहीं) केंद्रित करने की आवश्यकता है।

समस्या मेरी गणित इस बिंदु पर खो जाए है, और मुझे सही फार्मूला को समझ नहीं सकता इन स्क्रॉल बार समायोजित करने के लिए। मैंने गणना के कुछ अलग तरीकों की कोशिश की है, कुछ भी सही काम नहीं करता है।

यहाँ मेरी नियंत्रण का एक छीन संस्करण है। मैंने केवल प्रासंगिक सामग्री को हटा दिया है, मूल इकाई कोड की 600 से अधिक पंक्तियां हैं। नीचे सबसे महत्वपूर्ण प्रक्रिया SetZoom(const Value: Integer);

unit JD.Imaging; 

interface 

uses 
    Windows, Classes, SysUtils, Graphics, Jpeg, PngImage, Controls, Forms, 
    ExtCtrls, Messages; 

type 
    TJDImageBox = class; 

    TJDImageZoomEvent = procedure(Sender: TObject; const Zoom: Integer) of object; 

    TJDImageBox = class(TScrollingWinControl) 
    private 
    FZoom: Integer; //level of zoom by percentage 
    FPicture: TImage; //displays image within scroll box 
    FOnZoom: TJDImageZoomEvent; //called when zoom occurs 
    FZoomBy: Integer; //amount to zoom by (in pixels) 
    procedure MouseWheel(Sender: TObject; Shift: TShiftState; 
     WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); 
    procedure SetZoom(const Value: Integer); 
    procedure SetZoomBy(const Value: Integer); 
    public 
    constructor Create(AOwner: TComponent); override; 
    published 
    property Zoom: Integer read FZoom write SetZoom; 
    property ZoomBy: Integer read FZoomBy write SetZoomBy; 
    property OnZoom: TJDImageZoomEvent read FOnZoom write FOnZoom; 
    end; 

implementation 

{ TJDImageBox } 

constructor TJDImageBox.Create(AOwner: TComponent); 
begin 
    inherited Create(AOwner); 
    OnMouseWheel:= MouseWheel; 
    ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents, 
    csSetCaption, csDoubleClicks, csPannable, csGestures]; 
    AutoScroll := True; 
    TabStop:= True; 
    VertScrollBar.Tracking:= True; 
    HorzScrollBar.Tracking:= True; 
    Width:= 100; 
    Height:= 100; 
    FPicture:= TImage.Create(nil); 
    FPicture.Parent:= Self; 
    FPicture.AutoSize:= False; 
    FPicture.Stretch:= True; 
    FPicture.Proportional:= True; 
    FPicture.Left:= 0; 
    FPicture.Top:= 0; 
    FPicture.Width:= 1; 
    FPicture.Height:= 1; 
    FPicture.Visible:= False; 
    FZoom:= 100; 
    FZoomBy:= 10; 
end; 

destructor TJDImageBox.Destroy; 
begin 
    FImage.Free; 
    FPicture.Free; 
    inherited; 
end; 

procedure TJDImageBox.MouseWheel(Sender: TObject; Shift: TShiftState; 
    WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); 
var 
    NewScrollPos: Integer; 
begin 
    if ssCtrl in Shift then begin 
    if WheelDelta > 0 then 
     NewScrollPos := Zoom + 5 
    else 
     NewScrollPos:= Zoom - 5; 
    if NewScrollPos >= 5 then 
     Zoom:= NewScrollPos; 
    end else 
    if ssShift in Shift then begin 
    NewScrollPos := HorzScrollBar.Position - WheelDelta; 
    HorzScrollBar.Position := NewScrollPos; 
    end else begin 
    NewScrollPos := VertScrollBar.Position - WheelDelta; 
    VertScrollBar.Position := NewScrollPos; 
    end; 
    Handled := True; 
end; 

procedure TJDImageBox.SetZoom(const Value: Integer); 
var 
    Perc: Single; 
begin 
    FZoom := Value; 
    if FZoom < FZoomBy then 
    FZoom:= FZoomBy; 
    Perc:= FZoom/100; 
    //Resize picture to new zoom level 
    FPicture.Width:= Trunc(FImage.Width * Perc); 
    FPicture.Height:= Trunc(FImage.Height * Perc); 
    //Move scroll bars to properly position the center of the view 
    //This is where I don't know how to calculate the 'center' 
    //or by how much I need to move the scroll bars. 
    HorzScrollBar.Position:= HorzScrollBar.Position - (FZoomBy div 2); 
    VertScrollBar.Position:= VertScrollBar.Position - (FZoomBy div 2); 
    if assigned(FOnZoom) then 
    FOnZoom(Self, FZoom); 
end; 

procedure TJDImageBox.SetZoomBy(const Value: Integer); 
begin 
    if FZoomBy <> Value then begin 
    FZoomBy := EnsureRange(Value, 1, 100); 
    Paint; 
    end; 
end; 

end. 
+1

मैं यह भी कल्पना नहीं कर सकता कि "ज़ूम टू" बिंदु क्या करेगा। मैं एक बिंदु पर नहीं, एक आयताकार "ज़ूम" होगा। मैं अनुमान नहीं लगा सकता कि आपकी कक्षा का कार्यान्वयन कैसा दिखता है, इसलिए मैं अनुमान नहीं लगा सकता कि आपको किस गणित की आवश्यकता है, न ही कोई और। –

+0

@WarrenP मान लीजिए कि कई लोगों की एक तस्वीर प्रदर्शित होती है, माउस को किसी व्यक्ति के चेहरे के केंद्र में इंगित किया जाता है। जब उपयोगकर्ता नियंत्रण कुंजी रखता है और माउस व्हील को स्क्रॉल करता है, तो वह उस व्यक्ति के चेहरे पर ज़ूम करेगा, माउस पॉइंटर अभी भी तस्वीर की एक ही स्थिति में होगा। यही कारण है कि मैं 'प्वाइंट' पर ज़ूम कर रहा हूं, न कि 'Rect'। मुझे यकीन है कि मैं माउस घटनाओं को कैसे संभालने का प्रदर्शन करने के लिए उपर्युक्त सभी प्रासंगिक कोड शामिल करता हूं। –

उत्तर

4

यह स्पष्ट है कि तुम क्या एक्स के लिए उल्लेख करने के लिए चाहते हैं नहीं कर रहा है, वाई जब करने के लिए गुजर 'ZoomBy()' है। मुझे लगता है कि आपने छवि के लिए 'ऑनमोउसडाउन' हैंडलर रखा है और निर्देशांक संदर्भित करते हैं कि आप छवि पर कहां क्लिक करते हैं, यानी वे स्क्रॉलबॉक्स निर्देशांक के सापेक्ष नहीं हैं। यदि ऐसा नहीं है, तो आप इसे स्वयं ट्विक कर सकते हैं।

के एक मिनट के लिए जूमिंग के बारे में भूल करते हैं, हमारे काम मुद्दा यह है कि हम scrollbox में छवि पर क्लिक करें केंद्रित रहने दो। आसान, हम जानते हैं कि स्क्रॉलबॉक्स का केंद्र है (स्क्रॉलबॉक्स। क्लाइंटविड्थ/2, स्क्रॉलबॉक्स। क्लाइंटहाइट/2)।

procedure ScrollTo(CenterX, CenterY: Integer); 
begin 
    ScrollBox.HorzScrollBar.Position := CenterX - Round(ScrollBox.ClientWidth/2); 
    ScrollBox.VertScrollBar.Position := CenterY - Round(ScrollBox.ClientHeight/2); 
end; 


अब जूमिंग पर विचार करें: क्षैतिज सोचो, हम एक सीमा तक स्क्रॉल करने के लिए इतना है कि, हम यह करने के लिए ClientWidth/2 जोड़ते हैं, तो यह हमारे क्लिक बिंदु हो जाएगा चाहते हैं। हमें बस इतना करना है कि एक्स, वाई पदों की गणना करना है, स्क्रॉलबॉक्स का आकार नहीं बदलेगा। CenterX := Center.X * ZoomFactor। लेकिन सावधान रहें, 'ज़ूमफैक्टर' यहां प्रभावी ज़ूम नहीं है, यह ज़ूम है जब हम छवि पर क्लिक करेंगे। मुझे लगता है कि यह निर्धारित करने के लिए छवि के पहले और बाद में आयामों का उपयोग करेंगे:

procedure ZoomTo(CenterX, CenterY, ZoomBy: Integer); 
var 
    OldWidth, OldHeight: Integer; 
begin 
    OldWidth := FImage.Width; 
    OldHeight := FImage.Height; 

    // zoom the image, we have new image size and scroll range 

    CenterX := Round(CenterX * FImage.Width/OldWidth); 
    ScrollBox.HorzScrollBar.Position := CenterX - Round(ScrollBox.ClientWidth/2); 

    CenterY := Round(CenterY * FImage.Height/OldHeight); 
    ScrollBox.VertScrollBar.Position := CenterY - Round(ScrollBox.ClientHeight/2); 
end; 

बेशक, आप उन्हें एक लाइन में refactor चाहते हैं ताकि आप दौर() केवल एक बार फोन पूर्णांकन त्रुटि को कम करने।

मुझे यकीन है कि तुम यहाँ अपने आप से कसरत कर सकते हैं कर रहा हूँ।

+0

धन्यवाद, जब मैं घर वापस आऊंगा तो मैं इसे एक स्पिन दूंगा। –