定性调查中的鼠标跟踪
我编写了一个 Java 小程序来记录用户鼠标的位置并计算每个时间步的速度和加速度。我想将其转换为 JavaScript。这可能吗?
编辑2:我现在已经公开发布了代码,并编写了一个关于如何实现它的简短教程。
http://math.bu.edu/people/jackwalt/qualtrics-mousetracking/
欢迎使用和分享!
编辑1: 这是 Javascript 代码:
<input type="hidden" id="xPos" name="ED~xPos" value="" />
<input type="hidden" id="yPos" name="ED~yPos" value="" />
<input type="hidden" id="time" name="ED~time" value="" />
<script type="text/javascript">
var initTime = new Date().getTime();
document.onmousemove = getMousePosition;
function getMousePosition(mp) {
var divPos = getPosition(document.getElementById("mousearea"));
var event = [mp.pageX - divPos[0], mp.pageY - divPos[1], new Date().getTime() - initTime];
document.getElementById("xPos").value += event[0] + ", ";
document.getElementById("yPos").value += event[1] + ", ";
document.getElementById("time").value += event[2] + ", ";
return true;
}
function getPosition(obj){
var topValue= 0,leftValue= 0;
while(obj){
leftValue+= obj.offsetLeft;
topValue+= obj.offsetTop;
obj= obj.offsetParent;
}
return [leftValue, topValue];
}
</script>
这是 Java 代码:
主类:
package com.jackson.allgood;
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* This is a simple applet to record the position, velocity, and acceleration of a
* mouse pointer as it moves relative to names positioned in the window. Metadata is
* written to a text file and raw data to a .csv file.
*
* @author Jackson Walters
*
*/
public class Main extends Applet {
private static final long serialVersionUID = 1L;
private List<Data> dataList;
private MouseListenerThread testThread;
private long startTime;
private long endTime;
private static long DT = 10L;
private static final String[] NAMES = {"Name 1","Name 2","Name 3","Name 4"};
private static final String METADATA_FILENAME = "metadata";
private static final String DATA_FILENAME = "data";
/**
* Initializes the applet by creating an ArrayList to hold the data
* and starts a thread to begin collecting data.
*/
public void init() {
dataList = new ArrayList<Data>();
testThread = new MouseListenerThread();
testThread.start();
}
/**
* Stops the applet. Asks the thread to stop and waits until it does, then prints
* the metadata and data.
*/
public void stop(){
testThread.requestStop();
try {
testThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
printData();
printMetaData();
}
public void paint(Graphics g)
{
g.drawString(NAMES[0],getSize().width/2,20);
g.drawString(NAMES[1],getSize().width/2,getSize().height-20);
g.drawString(NAMES[2],10,getSize().height/2);
g.drawString(NAMES[3],getSize().width-100,getSize().height/2);
}
/**
* Thread that records the time and position of the mouse, calculates velocity and acceleration,
* then stores the data into the data list. Sleeps for the time interval specified by DT. Able to
* be stopped by setting a flag then waiting one time interval for the loop to exit.
*
*/
private class MouseListenerThread extends Thread{
private boolean running = true;
@Override
public void run(){
//initialize time and first data point
startTime = System.currentTimeMillis();
dataList.add(new Data(
0L,
MouseInfo.getPointerInfo().getLocation().getX(),
MouseInfo.getPointerInfo().getLocation().getY(),
0.,0.,0.,0.)
);
while(running){
long currentTime = System.currentTimeMillis()-startTime;
Point mousePos = MouseInfo.getPointerInfo().getLocation();
double xPos = mousePos.getX();
double yPos = mousePos.getY();
double xVel = (xPos-dataList.get(dataList.size()-1).getXPos())/DT;
double yVel = (yPos-dataList.get(dataList.size()-1).getYPos())/DT;
double xAcc = (xVel-dataList.get(dataList.size()-1).getXVel())/DT;
double yAcc = (yVel-dataList.get(dataList.size()-1).getYVel())/DT;
dataList.add(new Data(currentTime, xPos, yPos, xVel, yVel, xAcc, yAcc));
try {
sleep(DT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
endTime = System.currentTimeMillis();
}
public void requestStop(){
running = false;
}
}
/**
* Prints the raw data generated by the program to a .csv file.
*/
private void printData(){
try {
BufferedWriter out = new BufferedWriter(new FileWriter(DATA_FILENAME + ".csv"));
out.write(Data.DATA_HEADER_STRING);
for(Data data : dataList){
out.write(data.toString());
}
out.close();
} catch (Exception e){
System.err.println("Error: " + e.getMessage());
}
}
/**
* Prints general information about what the program did to a text file.
*/
private void printMetaData(){
try {
BufferedWriter out = new BufferedWriter(new FileWriter(METADATA_FILENAME + ".txt"));
out.write("Start time: " + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(startTime))); out.newLine();
out.write("End time: " + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(endTime))); out.newLine();
out.write("Elapsed time: " + formatElapsedTime(endTime-startTime)); out.newLine();
out.write("Number of measurments: " + dataList.size()); out.newLine();
out.write("Measurement interval: " + DT + "ms"); out.newLine(); out.newLine();
out.write("Names:"); out.newLine();
for(String name : NAMES){
out.write(name); out.newLine();
} out.newLine(); out.newLine();
out.close();
} catch (Exception e){
System.err.println("Error: " + e.getMessage());
}
}
/**
* Converts milliseconds in the format min:sec.
* @param ms
* @return Time as min:sec.
*/
private static String formatElapsedTime(long ms){
int seconds = (int) (ms/1000.);
int minutes = seconds/60;
seconds %= 60;
if(seconds < 10) return minutes + ":0" + seconds;
else return minutes + ":" + seconds;
}
}
实用程序类:
package com.jackson.allgood;
/**
* Simple class to store data such as time, position, velocity, and acceleration.
* @author Jackson Walters
*
*/
public class Data {
protected static final String DATA_HEADER_STRING = "Time, " +
"x position, " +
"y position, " +
"x velocity, " +
"y velocity, " +
"x acceleration, " +
"y acceleration" +
"\n";
private long time;
private double xPos;
private double yPos;
private double xVel;
private double yVel;
private double xAcc;
private double yAcc;
public Data(){}
public Data(long time, double xPos, double yPos, double xVel, double yVel, double xAcc, double yAcc){
this.time = time;
this.xPos = xPos;
this.yPos = yPos;
this.xVel = xVel;
this.yVel = yVel;
this.xAcc = xAcc;
this.yAcc = yAcc;
}
//getters and setters for time, pos, vel, and acc
public long getTime(){return time;}
public void setTime(long time){this.time = time;}
public double getXPos(){return xPos;}
public void setXPos(double xPos){this.xPos = xPos;}
public double getYPos(){return yPos;}
public void setYPos(double yPos){this.yPos = yPos;}
public double getXVel(){return xVel;}
public void setXVel(double xVel){this.xVel = xVel;}
public double getYVel(){return yVel;}
public void setYVel(double yVel){this.yVel = yVel;}
public double getXAcc(){return xAcc;}
public void setXAcc(double xAcc){this.xAcc = xAcc;}
public double getYAcc(){return yAcc;}
public void setYAcc(double yAcc){this.yAcc = yAcc;}
/**
* Formats the data as a string of comma separated values.
*/
@Override
public String toString(){
String toReturn = "";
toReturn += time + ", ";
toReturn += xPos + ", ";
toReturn += yPos + ", ";
toReturn += xVel + ", ";
toReturn += yVel + ", ";
toReturn += xAcc + ", ";
toReturn += yAcc + ", ";
toReturn += "\n";
return toReturn;
}
}
I've written a Java applet to record the position of a user's mouse and calculate the velocity and acceleration at each time step. I'd like to convert it to Javascript. Is this possible?
EDIT 2: I have now posted the code publicly and written a short tutorial on how to implement it.
http://math.bu.edu/people/jackwalt/qualtrics-mousetracking/
Feel free to use and share!
EDIT 1: Here's the Javascript code:
<input type="hidden" id="xPos" name="ED~xPos" value="" />
<input type="hidden" id="yPos" name="ED~yPos" value="" />
<input type="hidden" id="time" name="ED~time" value="" />
<script type="text/javascript">
var initTime = new Date().getTime();
document.onmousemove = getMousePosition;
function getMousePosition(mp) {
var divPos = getPosition(document.getElementById("mousearea"));
var event = [mp.pageX - divPos[0], mp.pageY - divPos[1], new Date().getTime() - initTime];
document.getElementById("xPos").value += event[0] + ", ";
document.getElementById("yPos").value += event[1] + ", ";
document.getElementById("time").value += event[2] + ", ";
return true;
}
function getPosition(obj){
var topValue= 0,leftValue= 0;
while(obj){
leftValue+= obj.offsetLeft;
topValue+= obj.offsetTop;
obj= obj.offsetParent;
}
return [leftValue, topValue];
}
</script>
Here's the Java code:
Main Class:
package com.jackson.allgood;
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* This is a simple applet to record the position, velocity, and acceleration of a
* mouse pointer as it moves relative to names positioned in the window. Metadata is
* written to a text file and raw data to a .csv file.
*
* @author Jackson Walters
*
*/
public class Main extends Applet {
private static final long serialVersionUID = 1L;
private List<Data> dataList;
private MouseListenerThread testThread;
private long startTime;
private long endTime;
private static long DT = 10L;
private static final String[] NAMES = {"Name 1","Name 2","Name 3","Name 4"};
private static final String METADATA_FILENAME = "metadata";
private static final String DATA_FILENAME = "data";
/**
* Initializes the applet by creating an ArrayList to hold the data
* and starts a thread to begin collecting data.
*/
public void init() {
dataList = new ArrayList<Data>();
testThread = new MouseListenerThread();
testThread.start();
}
/**
* Stops the applet. Asks the thread to stop and waits until it does, then prints
* the metadata and data.
*/
public void stop(){
testThread.requestStop();
try {
testThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
printData();
printMetaData();
}
public void paint(Graphics g)
{
g.drawString(NAMES[0],getSize().width/2,20);
g.drawString(NAMES[1],getSize().width/2,getSize().height-20);
g.drawString(NAMES[2],10,getSize().height/2);
g.drawString(NAMES[3],getSize().width-100,getSize().height/2);
}
/**
* Thread that records the time and position of the mouse, calculates velocity and acceleration,
* then stores the data into the data list. Sleeps for the time interval specified by DT. Able to
* be stopped by setting a flag then waiting one time interval for the loop to exit.
*
*/
private class MouseListenerThread extends Thread{
private boolean running = true;
@Override
public void run(){
//initialize time and first data point
startTime = System.currentTimeMillis();
dataList.add(new Data(
0L,
MouseInfo.getPointerInfo().getLocation().getX(),
MouseInfo.getPointerInfo().getLocation().getY(),
0.,0.,0.,0.)
);
while(running){
long currentTime = System.currentTimeMillis()-startTime;
Point mousePos = MouseInfo.getPointerInfo().getLocation();
double xPos = mousePos.getX();
double yPos = mousePos.getY();
double xVel = (xPos-dataList.get(dataList.size()-1).getXPos())/DT;
double yVel = (yPos-dataList.get(dataList.size()-1).getYPos())/DT;
double xAcc = (xVel-dataList.get(dataList.size()-1).getXVel())/DT;
double yAcc = (yVel-dataList.get(dataList.size()-1).getYVel())/DT;
dataList.add(new Data(currentTime, xPos, yPos, xVel, yVel, xAcc, yAcc));
try {
sleep(DT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
endTime = System.currentTimeMillis();
}
public void requestStop(){
running = false;
}
}
/**
* Prints the raw data generated by the program to a .csv file.
*/
private void printData(){
try {
BufferedWriter out = new BufferedWriter(new FileWriter(DATA_FILENAME + ".csv"));
out.write(Data.DATA_HEADER_STRING);
for(Data data : dataList){
out.write(data.toString());
}
out.close();
} catch (Exception e){
System.err.println("Error: " + e.getMessage());
}
}
/**
* Prints general information about what the program did to a text file.
*/
private void printMetaData(){
try {
BufferedWriter out = new BufferedWriter(new FileWriter(METADATA_FILENAME + ".txt"));
out.write("Start time: " + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(startTime))); out.newLine();
out.write("End time: " + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(endTime))); out.newLine();
out.write("Elapsed time: " + formatElapsedTime(endTime-startTime)); out.newLine();
out.write("Number of measurments: " + dataList.size()); out.newLine();
out.write("Measurement interval: " + DT + "ms"); out.newLine(); out.newLine();
out.write("Names:"); out.newLine();
for(String name : NAMES){
out.write(name); out.newLine();
} out.newLine(); out.newLine();
out.close();
} catch (Exception e){
System.err.println("Error: " + e.getMessage());
}
}
/**
* Converts milliseconds in the format min:sec.
* @param ms
* @return Time as min:sec.
*/
private static String formatElapsedTime(long ms){
int seconds = (int) (ms/1000.);
int minutes = seconds/60;
seconds %= 60;
if(seconds < 10) return minutes + ":0" + seconds;
else return minutes + ":" + seconds;
}
}
Utility Class:
package com.jackson.allgood;
/**
* Simple class to store data such as time, position, velocity, and acceleration.
* @author Jackson Walters
*
*/
public class Data {
protected static final String DATA_HEADER_STRING = "Time, " +
"x position, " +
"y position, " +
"x velocity, " +
"y velocity, " +
"x acceleration, " +
"y acceleration" +
"\n";
private long time;
private double xPos;
private double yPos;
private double xVel;
private double yVel;
private double xAcc;
private double yAcc;
public Data(){}
public Data(long time, double xPos, double yPos, double xVel, double yVel, double xAcc, double yAcc){
this.time = time;
this.xPos = xPos;
this.yPos = yPos;
this.xVel = xVel;
this.yVel = yVel;
this.xAcc = xAcc;
this.yAcc = yAcc;
}
//getters and setters for time, pos, vel, and acc
public long getTime(){return time;}
public void setTime(long time){this.time = time;}
public double getXPos(){return xPos;}
public void setXPos(double xPos){this.xPos = xPos;}
public double getYPos(){return yPos;}
public void setYPos(double yPos){this.yPos = yPos;}
public double getXVel(){return xVel;}
public void setXVel(double xVel){this.xVel = xVel;}
public double getYVel(){return yVel;}
public void setYVel(double yVel){this.yVel = yVel;}
public double getXAcc(){return xAcc;}
public void setXAcc(double xAcc){this.xAcc = xAcc;}
public double getYAcc(){return yAcc;}
public void setYAcc(double yAcc){this.yAcc = yAcc;}
/**
* Formats the data as a string of comma separated values.
*/
@Override
public String toString(){
String toReturn = "";
toReturn += time + ", ";
toReturn += xPos + ", ";
toReturn += yPos + ", ";
toReturn += xVel + ", ";
toReturn += yVel + ", ";
toReturn += xAcc + ", ";
toReturn += yAcc + ", ";
toReturn += "\n";
return toReturn;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这可能有点晚了,但我已经使用了一些鼠标和鼠标。带有 Qualtrics 的眼动追踪系统。主要问题是获取在 Qualtrics 域托管小型会话文件的权限。有一些现成的程序,例如 MouseFlow,效果很好,包括热图、完整视频和其他参数。我的大部分研究都是在实验室中进行的,我们控制了整个过程,但你的 JavaScript 想法听起来非常好。我很想听听你的进展如何。
This may be a little late but I have used a number of mouse & eye-tracking systems with Qualtrics. The main problem is getting access to host a small session file at the Qualtrics domain. There are a few off the shelf programs like MouseFlow that do a good job and include heat map, full video and other parameters. Most of my research has been in labs where we have controlled the whole process, but your JavaScript idea sounds really good. I would be very interested to hear how you get on.