2012-05-02 39 views
5

Tôi đang thực hiện một điều khiển đơn giản dựa trên số TScrollingWinControl (và mã được sao chép từ TScrollBox) với điều khiển TImage. Tôi phần nào có zooming để làm việc, nhưng nó không nhất thiết phải zoom đến một điểm tập trung - các thanh cuộn không thay đổi cho phù hợp để giữ cho tâm điểm tập trung.Phóng to/thu nhỏ một TImage bên trong TScrollBox đến một tiêu điểm cụ thể?

Tôi muốn có thể cho biết điều khiển này ZoomTo(const X, Y, ZoomBy: Integer); để cho biết nơi cần phóng to tiêu điểm. Vì vậy, khi nó phóng to, các tọa độ tôi đã vượt qua sẽ ở lại 'trung tâm'. Đồng thời, tôi cũng cần có một số ZoomBy(const ZoomBy: Integer); để cho nó được căn giữa trong chế độ xem hiện tại.

Ví dụ, sẽ có một trường hợp chuột được trỏ vào một điểm cụ thể của hình ảnh và khi giữ điều khiển và cuộn chuột lên, nó sẽ phóng to tập trung vào con trỏ chuột. Mặt khác, một trường hợp khác sẽ trượt điều khiển để điều chỉnh mức thu phóng, trong trường hợp đó, nó chỉ cần giữ tâm của chế độ xem hiện tại (không nhất thiết là tâm của hình ảnh) được lấy nét.

Vấn đề là toán của tôi bị mất vào thời điểm này và tôi không thể tìm ra công thức phù hợp để điều chỉnh các thanh cuộn này. Tôi đã thử một vài cách khác nhau để tính toán, không có gì có vẻ hoạt động đúng.

Đây là phiên bản bị tước quyền kiểm soát của tôi. Tôi đã xóa hầu hết các nội dung có liên quan, đơn vị ban đầu là hơn 600 dòng mã. Quy trình quan trọng nhất bên dưới là 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

Tôi thậm chí không thể tưởng tượng điểm "phóng to" sẽ làm gì. Tôi sẽ "phóng to" một hình chữ nhật, không phải là một điểm. Tôi không thể đoán được việc thực hiện lớp học của bạn trông như thế nào nên tôi không thể đoán được bạn cần toán học gì, cũng như không ai khác. –

+0

@WarrenP Giả sử ảnh của nhiều người được hiển thị, chuột được trỏ vào giữa khuôn mặt của một người. Khi người dùng giữ phím điều khiển và cuộn con lăn chuột lên, nó sẽ phóng to khuôn mặt của người đó, với con trỏ chuột vẫn ở cùng vị trí của hình ảnh. Đây là lý do tại sao tôi đang phóng to một 'Điểm' và không phải là một 'Rect'. Tôi khá chắc chắn rằng tôi đã bao gồm tất cả các mã có liên quan ở trên để minh họa cách tôi xử lý các sự kiện chuột. –

Trả lời

4

Không rõ bạn muốn giới thiệu gì cho X, Y khi chuyển đến 'ZoomBy()'. Tôi giả sử bạn đã đặt trình xử lý 'OnMouseDown' cho hình ảnh và tọa độ tham chiếu đến nơi bạn nhấp vào hình ảnh, tức là chúng không liên quan đến tọa độ hộp cuộn. Nếu điều này không phải như vậy, bạn có thể tự tinh chỉnh nó.

Hãy quên đi thu phóng trong một phút, để tác vụ của chúng tôi tập trung vào điểm mà chúng tôi nhấp vào hình ảnh trong hộp cuộn. Dễ dàng, chúng ta biết rằng trung tâm của hộp cuộn là lúc (ScrollBox.ClientWidth/2, ScrollBox.ClientHeight/2). Nghĩ ngang, chúng tôi muốn di chuyển lên đến một điểm do đó, nếu chúng ta thêm ClientWidth/2 đến nó, nó sẽ là điểm nhấn của chúng tôi:

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


Bây giờ xem xét phóng to. Tất cả những gì chúng ta phải làm là tính toán vị trí X, Y tương ứng, kích thước của hộp cuộn sẽ không thay đổi. CenterX := Center.X * ZoomFactor. Nhưng hãy cẩn thận, 'ZoomFactor' ở đây không phải là zoom hiệu quả, nó là zoom sẽ được áp dụng khi chúng ta nhấp vào hình ảnh. Tôi sẽ sử dụng hình ảnh trước và sau các thứ nguyên để xác định rằng:

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; 

Tất nhiên, bạn nên cấu trúc lại thành một hàng để bạn gọi Vòng() một lần để giảm lỗi làm tròn.

Tôi chắc rằng bạn có thể tự mình tập luyện ở đây.

+0

Cảm ơn, tôi sẽ cung cấp cho nó một spin khi tôi trở về nhà. –

Các vấn đề liên quan