Il presente articolo è stato tradotto automaticamente.

Nuove frontiere per l'interfaccia utente

Inerzia nella tecnologia multitouch

Charles Petzold

Scaricare il codice di esempio

Interfacce utente sui computer sembrano essere molto più interessante quando mascherare la natura digitale della tecnologia sottostante, invece di simulare l'aspetto analogico del mondo reale. Questa tendenza iniziata all'avvio di interfacce grafiche sostituendo le righe di comando e quindi continua con l'aumento dell'utilizzo di fotografie, suoni e altri file multimediali.

L'integrazione di multi-touch in schermi video ha fornito un ulteriore aumento al processo di creazione di controlli utente più intuitiva e lifelike. Penso che solo già iniziato a glimpse potenzialmente rivoluzione tocco revamp la relazione con lo schermo del computer.

Forse un giorno ci saranno anche eliminare uno di vestiges ugliest della tecnologia digitale noto uomo: controllo volume push-button. La connessione base controllata volume (e altre impostazioni) radio e televisori delightfully semplice, ma è stato sostituito con i pulsanti di comando difficile telecomandi e apparecchi stessi.

Interfacce di computer offrono spesso i dispositivi di scorrimento per il controllo del volume. Questi sono quasi altrettanto soddisfacente compone. Ma i controlli volume push-button sono ancora persistenti. Anche lo schermo multi-touch di Zune HD richiede di premere segno più e segno meno per aumentare e diminuire il volume. Più preferibile sarebbe una connessione che risponde al tocco.

Mi piace la connessione per multi-touch. Forse Ecco perché mi sono concentrato su simulazione compone nel mio articolo “ Finger Style: Exploring Multi-Touch Support in Silverlight,” in the March 2010 issue of MSDN Magazine (msdn.microsoft.com/magazine/ee336026), and why I’m returning to dials to talk about handling multi-touch inertia in the Windows Presentation Foundation (WPF).

Evento inerzia

Uno dei modi in cui un'interfaccia multi-touch tenta di simulare il mondo reale è l'introduzione a inerzia, la tendenza per oggetti mantenere la stessa velocità, a meno che non venga effettuata da altre forze, ad esempio semplice. Nelle interfacce multi-touch, a inerzia può mantenere un oggetto dello spostamento quando le dita rimasti sullo schermo. L'applicazione più comune a inerzia tocco è esplorare elenchi e a inerzia è già incorporato nel WPF ListBox.

Tra il codice scaricabile di questo articolo è un piccolo programma chiamato IntertialListBoxDemo compila semplicemente un controllo ListBox di WPF con una serie di elementi così è possibile eseguirne il test fuori dello schermo multi-touch.

I controlli WPF, è necessario decidere quanta a inerzia desiderato, se presente. La gestione a inerzia è più semplice se si desidera semplicemente a inerzia causa la stessa risposta dal programma di modifica diretta. La parte difficile è ottenere una buona conoscenza dei valori numerici coinvolti.

Come si è visto negli articoli precedenti, un elemento WPF viene informato di set di manipolazione multi-touch con due eventi: ManipulationStarting (che è un'ottima opportunità per l'esecuzione di alcuni inizializzazione) e ManipulationStarted. Questi due eventi sono quindi seguiti da potenzialmente molti eventi di ManipulationDelta, consolidare l'azione di uno, due o più dita in informazioni di conversione, ridimensionamento e rotazione.

Quando tutte le dita hanno sollevato dall'elemento modificato, viene generato un evento ManipulationInertiaStarting. Si tratta consente del programma di specificare la quantità desiderata a inerzia. (Tratterò, poco.) L'effetto di a inerzia assume la forma di altri eventi ManipulationDelta fino a quando l'oggetto “ lungo ” e un evento ManipulationCompleted segnala la fine. Se non con ManipulationInertiaStarting, è seguito immediatamente da ManipulationCompleted.

L'utilizzo dell'evento ManipulationDelta per indicare modifiche dirette e a inerzia rende estremamente semplice da utilizzare questo schema intero. In sostanza, è possibile implementare a inerzia con una singola istruzione nell'evento ManipulationInertiaStarting. Se necessario, è possibile distinguere la differenza tra la modifica diretta e a inerzia dalla proprietà IsInertial di ManipulationDeltaEventArgs.

Come si vedrà, specificare la quantità desiderata in due modi diversi a inerzia, ma comportano entrambi decelerazione, una diminuzione della velocità in un periodo di tempo finché la velocità raggiunge zero e l'oggetto viene interrotto lo spostamento. Ciò comporta alcuni concetti di fisica molti programmatori non riscontrano spesso, forse che un corso di aggiornamento poco utili.

Un aggiornamento accelerazione

If something is moving, it is said to have a speed or velocity that can be expressed in units of distance-per-time. If the velocity is itself changing over the course of time, then the object has acceleration. A negative acceleration (indicating decreasing velocity) is often called deceleration.

Tutta questa discussione let’s presuppongono che l'accelerazione o decelerazione stesso è costante. Una costante accelerazione significa che la velocità cambia in modo lineare, ovvero un'uguale quantità per unità di tempo. Accelerazione è 0, la velocità rimane costante.

Ad esempio, produttori automobile talvolta annunciare le automobili come in grado di accelerare da 0 a 60 Km/h in un certo numero di secondi, pronunciare let’s 8 secondi. L'automobile è inizialmente inattivi, ma al termine di 8 secondi, verrà a 60 Km/h, come illustrato in di Figura 1.

Figura 1 di Accelerazione

Secondi Velocità
0 0 km/h
1 7.5
2 15
3 22.5
4 30
5 37.5
6 45
7 52.5
8 60

Si noti che la velocità aumenta lo stesso importo ogni secondo. L'accelerazione è espresso come la modifica della velocità durante un determinato periodo di tempo o in questo caso, 7,5 km/h al secondo.

Tale valore accelerazione è un po' difficile perché sono coinvolti due unità di tempo: ore e secondi. Let’s togliere ore e semplicemente utilizzare secondi. Poiché 60 Km/h 88 metri al secondo, l'automobile può essere detto allo stesso modo per passare da 0 a piedi 88 8 secondi in un secondo. The acceleration is 11 feet per second per second, or (as it’s often phrased) 11 feet per second squared.

È possibile tornare a chilometri e ore, ma l'unità ottenere ridicolo come 27,000 km/h al quadrato viene calcolato l'accelerazione. Questo significa che alla fine dell'ora, l'automobile è viaggio 27,000 km/h. Naturalmente non farlo. In realtà, appena l'automobile ottiene a 60 Km/h o thereabouts, livelli di la velocità e l'accelerazione si arresta su 0. That’s a change in acceleration, which in engineering circles is referred to as a jerk.

Tuttavia in questo esercizio si suppone costante accelerazione. Starting at a velocity of 0, as a result of acceleration a, the distance x traveled during time t is:

image: equation, x equals one-half a t squared

The ½ and square are necessary because the velocity v is calculated as the first derivative of the distance with respect to time:

image: equation, v equals d x over d t equals a t

If a is 11 feet per second squared, then at tequal to 1 second, the velocity is 11 feet per second, but the car has only traveled 5.5 feet. At t equal to 2 seconds, the velocity is 22 feet per second, and the car has traveled a total of 22 feet. Durante ogni secondo, l'automobile viaggia a distanza basata sulla velocità media durante tale secondo. At tequal to 8 seconds, the velocity is 88 feet per second, and the car has traveled a total of 352 feet.

A inerzia Multi-Touch è simile a guardare l'automobile in ordine inverso: Al momento che le dita sollevare dallo schermo, l'oggetto presenta una determinata velocità. L'applicazione specifica una decelerazione determina la velocità dell'oggetto per diminuire in modo lineare a 0. Maggiore la decelerazione interrompe il più veloce l'oggetto. Una decelerazione 0 provoca l'oggetto continuare a spostare sempre con la stessa velocità.

Due modi per Decelerate

Gli argomenti dell'evento ManipulationDelta includono un Velocities proprietà del tipo ManipulationVelocities, che a sua volta ha tre proprietà corrispondenti ai tre tipi di manipolazione:

  • LinearVelocity di tipo vettoriale
  • ExpansionVelocity di tipo vettoriale
  • AngularVelocity di tipo double

Le prime due proprietà sono espressi come unità indipendenti dalla periferica al millisecondo;il terzo è angoli (in gradi) al millisecondo. Naturalmente, non è comprensibile in modo intuitivo al millisecondo periodo di tempo, pertanto se si desidera esaminare questi valori e farsi un'idea del relativo significato, è necessario moltiplicare 1.000 per convertire in secondi.

Le applicazioni spesso non rendere utilizzare queste proprietà velocità, ma offrono velocities iniziale per a inerzia. Quando dita dell'utente lascia sullo schermo, si verifica un evento ManipulationInertiaStarting. Gli argomenti dell'evento includono queste tre proprietà che consentono di specificare a inerzia separatamente per le stessi tre tipi di manipolazione:

  • TranslationBehavior di tipo InertiaTranslationBehavior
  • ExpansionBehavior di tipo InertiaExpansionBehavior
  • RotationBehavior di tipo InertiaRotationBehavior

Ciascuna di queste classi è una proprietà InitialVelocity, una proprietà DesiredDeceleration e una terza proprietà denominato DesiredDisplacement, DesiredExpansion o DesiredRotation, a seconda della classe.

Per la conversione e rotazione, tutte le proprietà desiderato hanno valori predefiniti di Double.NaN, tale configurazione speciale bit indica “ non numero. ” Per l'espansione, le proprietà desiderato sono di tipo vettoriale con X e Y valori Double.NaN, ma il concetto è lo stesso.

Let’s concentrarsi su a inerzia rotazionale in primo luogo, poiché si tratta di un effetto familiare dal mondo reale (ad esempio un campo rotonda) e non è necessario preoccuparsi di oggetti volanti fuori dallo schermo.

Quando che si riceve un evento ManipulationInertiaStarting, è stato creato l'oggetto InertiaRotationBehavior e dall'evento ManipulationDelta precedente è stato impostato il valore InitialVelocity. Se il InitialVelocity 1.08, ad esempio, che è 1.08 gradi al millisecondo, o gradi 1,080 al secondo o tre giri al secondo o 180 rpm.

Per mantenere l'oggetto Ruota, impostare DesiredRotation o DesiredDeceleration ma non entrambi. Se si tenta di impostare entrambi si imposta l'ultimo sarà valido e l'altro sarà Double.NaN.

La prima opzione consiste nell'impostare DesiredRotation su un valore espresso in gradi. Questo è il numero di gradi, che l'oggetto verrà girare prima si interrompe. Se si imposta DesiredRotation 360, ad esempio, l'oggetto rotante renderà solo una rivoluzione ulteriore rallentamento e arresto del processo. Il vantaggio è ottenere la stessa quantità di attività inertial indipendentemente dalla velocità iniziale, pertanto è facile prevedere che cosa accadrà. Lo svantaggio è che non è completamente naturale.

In alternativa è possibile impostare un valore in unità di gradi al millisecondo al quadrato DesiredDeceleration ed ecco dove risulta un po' delicato perché è difficile immaginare un valore valido.

Se InitialVelocity è 1.08 gradi al millisecondo e si imposta DesiredDeceleration a 0,01 gradi al millisecondo al quadrato, quindi la velocità diminuirà di 0,01 gradi ogni millisecondi: 1.07 gradi al millisecondo in fondo al primo millisecondo, gradi 1.06 al millisecondo alla fine del secondo millisecondo e così via in modo lineare finché la velocità ottiene 0. Intero processo richiederà ms 108 o poco più di un decimo di secondo.

È consigliabile impostare DesiredDeceleration su un valore inferiore a quello che forse 0.001 gradi al millisecondo al quadrato, causando l'oggetto continuare rotante 1.08 secondi.

Controllare se si desidera convertire le unità decelerazione per un po' più semplice per la mente umana a contemplate qualcosa. La velocità di gradi 1.08 al millisecondo equivale 1,080 gradi al secondo, ma una decelerazione di gradi 0.001 al millisecondo al quadrato è 1.000 gradi al secondo al quadrato. To convert deceleration to seconds you need to multiply by 1,000 twice because time is squared.

If you combine the two formulas shown earlier and eliminate t, you get:

Image: equation, a equals v squared over 2 x

Questo implica che i due metodi per l'impostazione decelerazione sono equivalenti e possono essere convertiti da uno a altro. Se il InitialVelocity è 1.08 gradi al millisecondo e si imposta il DesiredDeceleration 0.001 gradi al millisecondo al quadrato, che è equivalente all'impostazione DesiredRotation 583.2 gradi. In entrambi i casi, rotazione si arresta dopo 1,080 ms.

Sperimentazione

Per ottenere un aspetto a inerzia rotazionale, ho creato il programma RotationalInertiaDemo mostrato in di Figura 2.

image: RotationalInertiaDemo Program in Action

Figura 2 di Programma RotationalInertiaDemo in azione

Ruota a sinistra è attiva con il dito, in senso orario o in senso antiorario. È molto semplice: solo un derivato UserControl con due elementi Ellipse in una griglia con tutti gli eventi modifica in MainWindow gestiti.

Evento ManipulationStarting esegue l'inizializzazione limitando la manipolazione di rotazione solo, consentendo il dito singola rotazione e impostando il centro di rotazione:

args.IsSingleTouchEnabled = true;
args.Mode = ManipulationModes.Rotate;
args.Pivot = new ManipulationPivot(
             new Point(ctrl.ActualWidth / 2, 
                       ctrl.ActualHeight / 2), 50);

Speedometer a destra è una classe denominata ValueMeter e Mostra la velocità corrente del selettore. Se appare vaguely familiare, è solo perché è una versione avanzata del modello ProgressBar da un articolo che scritto più di tre anni di questa rivista. I miglioramenti comportava una certa flessibilità con le etichette in modo da poter utilizzare per visualizzare la velocità in quattro diverse unità. GroupBox della finestra consentono di selezionare tali unità.

Quando il dito è ruotando la connessione, l'indicatore mostra la velocità corrente di angular sottoproprietà Velocities.AngularVelocity di argomenti dell'evento ManipulationDelta disponibile. Ma ho scoperto che non era possibile imbuto la velocità di connessione direttamente a ValueMeter. Il risultato era troppo allo. È stato necessario scrivere una piccola classe ValueSmoother per eseguire una media ponderata di tutti i valori secondo trimestre precedente. Il gestore eventi ManipulationDelta imposta inoltre un oggetto RotateTransform Ruota effettivamente la connessione:

rotate.Angle += args.DeltaManipulation.Rotation;

Infine, il dispositivo di scorrimento nella parte inferiore consente di selezionare un valore decelerazione. Il valore dello Slider viene letto solo quando il dito lascia la connessione e viene generato un evento ManipulationInertiaStarted:

args.RotationBehavior.DesiredDeceleration = slider.Value;

È l'intero corpo del gestore eventi ManipulationInertiaStarted. Durante la fase inertial di manipolazione, i valori di velocità sono molto più regolari e non devono essere resa uniforme, in modo che il gestore ManipulationDelta utilizza la proprietà IsInertial per stabilire quando passare valori di velocità direttamente il misuratore.

Limiti e Bounce indietro

L'utilizzo più comune di a inerzia con multi-touch è lo spostamento di oggetti sullo schermo come scorrere un elenco lungo o flicking elementi sul lato. Il grande problema è che è molto facile da causare un elemento passare direttamente spento volanti sullo schermo!

Ma il processo di gestione di tale problema, si scoprirà che WPF è una funzionalità incorporata che rende ancora più realistico a inerzia manipolazione. Si sarà notato in programma ListBoxDemo che quando si consente a inerzia scorrere fino alla fine o all'inizio dell'elenco, la finestra intera animazione leggermente quando il controllo ListBox raggiunge la fine. Che è anche un effetto (se si desidera) è possibile ottenere nelle proprie applicazioni.

Il programma BoundaryDemo è costituito da un ellisse con un'identità che MatrixTransform impostata alla proprietà RenderTransform seduta in un pannello Grid denominato mainGrid. Solo traduzione è attivato durante l'override OnManipulationStarting. La decelerazione di OnManipulationInertiaStarting metodo set a inerzia nel modo seguente:

args.TranslationBehavior.DesiredDeceleration = 0.0001;

Unità indipendenti dalla periferica a 0,0001 al millisecondo al quadrato, o 100 unità indipendenti dalla periferica per secondo al quadrato o su 1 pollice per ogni secondo al quadrato.

Di Figura 3 è illustrato l'override OnManipulationDelta. Si noti la gestione speciale del IsInertial è true. L'idea alla base di codice è che i fattori di conversione devono essere attenuated quando l'ellisse ha letto parzialmente fuori dallo schermo. Il fattore di attenuazione è 0 se l'ellisse in mainGrid e va 1 fino a quando l'ellisse si trova a metà oltre i limiti del mainGrid. Il fattore di attenuazione viene quindi applicato il vettore di traslazione recapitato al metodo (chiamato totalTranslate) per calcolare usableTranslate, fornisce i valori effettivamente applicati alla matrice di trasformazione.

Figura 3 L'evento OnManipulationDelta in BoundaryDemo

protected override void OnManipulationDelta(
  ManipulationDeltaEventArgs args) {

  FrameworkElement element = 
    args.Source as FrameworkElement;
  MatrixTransform xform = 
    element.RenderTransform as MatrixTransform;
  Matrix matx = xform.Matrix;
  Vector totalTranslate = 
    args.DeltaManipulation.Translation;
  Vector usableTranslate = totalTranslate;

  if (args.IsInertial) {
    double xAttenuation = 0, yAttenuation = 0, attenuation = 0;

    if (matx.OffsetX < 0)
      xAttenuation = -matx.OffsetX;
    else
      xAttenuation = matx.OffsetX + 
        element.ActualWidth - mainGrid.ActualWidth;

    if (matx.OffsetY < 0)
      yAttenuation = -matx.OffsetY;
    else
      yAttenuation = matx.OffsetY + 
        element.ActualHeight - mainGrid.ActualHeight;

    xAttenuation = Math.Max(0, Math.Min(
      1, xAttenuation / (element.ActualWidth / 2)));

    yAttenuation = Math.Max(0, Math.Min(
      1, yAttenuation / (element.ActualHeight / 2)));

    attenuation = Math.Max(xAttenuation, yAttenuation);

    if (attenuation > 0) {
      usableTranslate.X = 
        (1 - attenuation) * totalTranslate.X;
      usableTranslate.Y = 
        (1 - attenuation) * totalTranslate.Y;

      if (totalTranslate != usableTranslate)
        args.ReportBoundaryFeedback(
          new ManipulationDelta(totalTranslate – 
          usableTranslate, 0, new Vector(), new Vector()));

      if (attenuation > 0.99)
        args.Complete();
    }
  }
  matx.Translate(usableTranslate.X, usableTranslate.Y);
  xform.Matrix = matx;

  args.Handled = true;
  base.OnManipulationDelta(args);
}

L'effetto risultante è che l'ellisse non interrompe immediatamente quando raggiunge il limite, ma rallenta notevolmente come se incontrare alcuni sludge spessa.

L'override OnManipulationDelta chiama inoltre il metodo ReportBoundaryFeedback, passando la parte inutilizzata il vettore di traslazione è totalTranslate vettore meno usableTranslate.Per impostazione predefinita, questo viene elaborato per rendere la finestra Rimbalzo un po' come rallenta l'ellisse, dimostrando il principio fisica che per ogni azione vi è una reazione opposto e uguale.

Questo effetto funziona meglio quando la velocità di impatto è abbastanza grande.Se l'ellisse è rallentato appreciably verso il basso, è un effetto di Vibrazione meno soddisfacente, ma è un elemento desiderato per controllare in modo più dettagliato.Se si non desidera affatto l'effetto o desidera soltanto in alcuni casi, è possibile evitare semplicemente chiamare ReportBoundaryFeedback.Oppure è possibile gestire l'evento ManipulationBoundaryFeedback personalmente.È possibile impedire predefinito di elaborazione impostando la proprietà Handled di argomenti dell'evento su true oppure scegliere un altro approccio.Ho lasciato metodo OnManipulationBoundaryFeedback vuoto nel programma per la sperimentazione.

Charles Petzold* esperti redattore di MSDN Magazine.Egli è attualmente la scrittura “ Programming Windows telefono 7 ” (Microsoft Press), che verrà pubblicato come un e-book scaricabile gratuitamente nell'autunno del 2010.Una versione di anteprima è disponibile tramite di charlespetzold.comil suo sito Web.*

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Doug Kramer e di Robert tassa