首页
/ 3个高效步骤:使用jsPsych构建专业行为实验的完整指南

3个高效步骤:使用jsPsych构建专业行为实验的完整指南

2026-04-10 09:47:42作者:曹令琨Iris

问题:行为实验开发的核心挑战

在行为科学研究中,研究者常常面临三大核心挑战:如何精确测量反应时数据、如何确保实验在不同设备上的一致性、以及如何高效处理实验流程中的复杂逻辑。传统实验开发往往需要专业的编程知识和繁琐的设备配置,这使得许多研究者将大量时间耗费在技术实现而非实验设计本身。

jsPsych作为一款专为行为实验设计的JavaScript库,通过插件化架构和精确的计时系统,为解决这些问题提供了完整方案。本指南将采用"问题-方案-实践"框架,帮助你快速掌握jsPsych的核心功能,构建专业、可靠的行为实验。

方案:模块化实验开发路径

1. 环境搭建与核心概念

1.1 快速安装与项目配置

需求场景:从零开始搭建jsPsych实验环境,确保所有依赖正确配置。

技术原理:jsPsych采用模块化设计,核心库提供基础功能,插件扩展特定实验范式。通过npm安装可确保依赖管理的规范性。

代码实现:

# 克隆官方仓库
git clone https://gitcode.com/gh_mirrors/js/jsPsych

# 进入项目目录
cd jsPsych

# 安装依赖
npm install

# 启动开发服务器
npm run start

效果验证:访问http://localhost:8080/examples/,查看示例实验是否正常运行。

1.2 实验基本结构

需求场景:理解jsPsych实验的基本组成部分,创建第一个最小化实验。

技术原理:每个jsPsych实验由timeline(时间线)和trial(试次)构成,通过插件定义不同类型的实验任务。

代码实现:

<!DOCTYPE html>
<html>
<head>
  <title>最小化jsPsych实验</title>
  <script src="node_modules/jspsych/dist/jspsych.js"></script>
  <link rel="stylesheet" href="node_modules/jspsych/css/jspsych.css">
</head>
<body></body>
<script>
  // 创建欢迎试次
  const welcome = {
    type: 'html-keyboard-response',
    stimulus: '欢迎参加实验!按任意键开始。',
    post_trial_gap: 500 // 试次间间隔500毫秒
  };

  // 创建实验试次
  const trial = {
    type: 'html-keyboard-response',
    stimulus: '看到"GO"按空格键,看到"NO"不按键',
    choices: [' '], // 只允许空格键反应
    trial_duration: 1500, // 1500毫秒后自动进入下一试次
    data: {task: 'go_no_go'} // 添加自定义数据字段
  };

  // 创建结束试次
  const goodbye = {
    type: 'html-keyboard-response',
    stimulus: '实验结束,谢谢参与!',
    choices: jsPsych.NO_KEYS // 不需要按键反应
  };

  // 构建时间线
  const timeline = [welcome, trial, goodbye];

  // 初始化实验
  jsPsych.init({
    timeline: timeline,
    on_finish: function() {
      // 实验结束后显示数据
      jsPsych.data.displayData();
    }
  });
</script>
</html>

效果验证:在浏览器中打开该HTML文件,观察实验流程是否符合预期,结束后是否正确显示数据。

1.3 术语卡片:Timeline与Trial

定义:Timeline是实验流程的有序集合,由一个或多个Trial组成;Trial是实验的基本单元,定义单次实验任务。

使用场景:所有jsPsych实验都基于Timeline组织,复杂实验可通过嵌套Timeline实现分支逻辑。

注意事项

  • Timeline中的试次按顺序执行
  • 可通过conditional_function实现条件分支
  • 可通过loop_function实现循环结构
  • 试次间默认无间隔,需通过post_trial_gap设置

2. 核心功能实现

2.1 反应时测量实验

需求场景:创建一个视觉刺激反应时实验,精确测量从刺激呈现到按键反应的时间。

技术原理:jsPsych的键盘反应插件提供毫秒级精度的反应时记录,通过stimulus参数呈现刺激,choices参数限制允许的按键。

代码实现:

// 定义实验材料
const stimuli = [
  {word: '红色', color: 'red'},
  {word: '蓝色', color: 'blue'},
  {word: '绿色', color: 'green'},
  {word: '黄色', color: 'yellow'}
];

// 创建 Stroop 实验试次
const stroop_trial = {
  type: 'html-keyboard-response',
  stimulus: function() {
    // 随机选择一个刺激
    const stimulus = jsPsych.randomization.sampleWithoutReplacement(stimuli, 1)[0];
    // 保存刺激信息到数据中
    jsPsych.data.addDataToLastTrial({
      word: stimulus.word,
      color: stimulus.color,
      congruent: stimulus.word === stimulus.color
    });
    // 返回带颜色的文字刺激
    return `<div style="font-size: 48px; color: ${stimulus.color};">${stimulus.word}</div>`;
  },
  choices: ['r', 'b', 'g', 'y'], // 分别对应红、蓝、绿、黄
  prompt: `<p>请按对应颜色的键:R=红,B=蓝,G=绿,Y=黄</p>`,
  data: {task: 'stroop'},
  on_finish: function(data) {
    // 判断反应是否正确
    const correct_key = {
      'red': 'r',
      'blue': 'b',
      'green': 'g',
      'yellow': 'y'
    }[data.color];
    data.correct = data.response === correct_key;
  }
};

// 创建实验时间线
const timeline = [
  {
    type: 'html-keyboard-response',
    stimulus: 'Stroop实验准备开始!按任意键继续。'
  },
  {
    timeline: [stroop_trial],
    repetitions: 12, // 重复12次实验试次
    randomize_order: true // 随机化试次顺序
  },
  {
    type: 'html-keyboard-response',
    stimulus: function() {
      const data = jsPsych.data.get().filter({task: 'stroop'});
      const correct = data.filter({correct: true}).count();
      const accuracy = (correct / data.count() * 100).toFixed(1);
      const rt = data.filter({correct: true}).select('rt').mean().toFixed(0);
      return `实验结束!<br>正确率: ${accuracy}%<br>平均反应时: ${rt}ms`;
    }
  }
];

// 初始化实验
jsPsych.init({
  timeline: timeline,
  show_progress_bar: true,
  message_progress_bar: '实验进度'
});

效果验证:运行实验,观察是否正确呈现颜色词刺激,结束后是否显示正确的正确率和平均反应时。

jsPsych实验进度条示例 带有进度条的Stroop实验界面,显示当前完成进度和当前试次刺激

2.2 多设备响应式设计

需求场景:确保实验在桌面和移动设备上都能正常运行,界面元素自适应不同屏幕尺寸。

技术原理:jsPsych内置响应式设计支持,结合CSS媒体查询可实现完全适配各种设备的实验界面。

代码实现:

<!DOCTYPE html>
<html>
<head>
  <title>响应式jsPsych实验</title>
  <script src="node_modules/jspsych/dist/jspsych.js"></script>
  <link rel="stylesheet" href="node_modules/jspsych/css/jspsych.css">
  <style>
    /* 基础样式 */
    .jspsych-content {
      max-width: 800px;
    }
    
    /* 刺激样式 */
    .stimulus {
      font-size: 4vw; /* 使用视窗宽度单位,自动适应屏幕 */
      margin: 20px 0;
    }
    
    /* 按钮样式 */
    .jspsych-btn {
      padding: 15px 30px;
      font-size: 1.2em;
      margin: 10px;
    }
    
    /* 移动设备适配 */
    @media (max-width: 768px) {
      .stimulus {
        font-size: 8vw;
      }
      
      .jspsych-btn {
        display: block;
        width: 80%;
        margin: 10px auto;
        padding: 12px;
      }
    }
  </style>
</head>
<body></body>
<script>
  // 浏览器检查试次
  const browser_check = {
    type: 'browser-check',
    minimum_width: 320,
    minimum_height: 480,
    message: '您的设备或浏览器不支持本实验,请使用屏幕尺寸更大的设备或更新浏览器。'
  };

  // 响应式问卷试次
  const survey_trial = {
    type: 'survey-likert',
    questions: [
      {
        prompt: '这个实验界面在您的设备上显示是否正常?',
        labels: ['非常不正常', '不太正常', '一般', '比较正常', '非常正常'],
        required: true
      },
      {
        prompt: '您使用的设备类型是?',
        labels: ['手机', '平板', '笔记本电脑', '台式电脑'],
        required: true
      }
    ],
    randomize_question_order: false
  };

  // 时间线
  const timeline = [browser_check, survey_trial];

  // 初始化实验
  jsPsych.init({
    timeline: timeline,
    on_finish: function() {
      jsPsych.data.displayData();
    }
  });
</script>
</html>

效果验证:在不同设备或浏览器开发者工具的不同设备模式下测试,观察界面元素是否正确适配。

jsPsych调查插件在移动设备和桌面设备上的显示效果 jsPsych调查插件自动适配移动和桌面设备,确保实验在各种终端上都有良好表现

2.3 数据收集与导出

需求场景:收集实验数据并以多种格式导出,便于后续分析。

技术原理:jsPsych提供全面的数据API,可获取、筛选和转换实验数据,支持CSV、JSON等多种格式导出。

代码实现:

// 定义简单的反应时实验
const trial = {
  type: 'image-keyboard-response',
  stimulus: function() {
    return jsPsych.randomization.sampleWithoutReplacement(
      ['examples/img/blue.png', 'examples/img/orange.png'], 1
    )[0];
  },
  choices: ['f', 'j'],
  prompt: '<p>看到蓝色按F键,看到橙色按J键</p>',
  data: {task: 'color_discrimination'}
};

// 实验结束处理
const end_experiment = {
  type: 'html-keyboard-response',
  stimulus: '实验结束!按任意键查看结果。',
  on_finish: function() {
    // 获取所有实验数据
    const all_data = jsPsych.data.get();
    
    // 筛选特定任务数据
    const task_data = all_data.filter({task: 'color_discrimination'});
    
    // 计算基本统计量
    const accuracy = task_data.filter({correct: true}).count() / task_data.count() * 100;
    const mean_rt = task_data.filter({correct: true}).select('rt').mean();
    
    // 显示结果
    const results = `
      <h2>实验结果</h2>
      <p>正确率: ${accuracy.toFixed(1)}%</p>
      <p>平均反应时: ${mean_rt.toFixed(0)}ms</p>
      <button onclick="downloadCSV()">下载CSV数据</button>
      <button onclick="downloadJSON()">下载JSON数据</button>
    `;
    
    document.querySelector('#jspsych-content').innerHTML = results;
    
    // 定义下载函数
    window.downloadCSV = function() {
      const csv = all_data.csv();
      const blob = new Blob([csv], {type: 'text/csv'});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'experiment_data_' + new Date().toISOString().slice(0,10) + '.csv';
      a.click();
    };
    
    window.downloadJSON = function() {
      const json = all_data.json();
      const blob = new Blob([json], {type: 'application/json'});
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'experiment_data_' + new Date().toISOString().slice(0,10) + '.json';
      a.click();
    };
  }
};

// 时间线
const timeline = [
  {
    type: 'html-keyboard-response',
    stimulus: '颜色辨别实验准备开始!按任意键继续。'
  },
  {
    timeline: [trial],
    repetitions: 10,
    randomize_order: true
  },
  end_experiment
];

// 初始化实验
jsPsych.init({
  timeline: timeline
});

效果验证:完成实验后,点击下载按钮,检查是否成功生成包含所有试次数据的CSV和JSON文件。

3. 进阶拓展与优化

3.1 实验流程控制

需求场景:实现基于被试表现的自适应实验流程,如根据正确率调整难度。

技术原理:通过conditional_function和loop_function实现条件分支和循环控制,动态调整实验流程。

代码实现:

// 定义不同难度的刺激
const easy_stimuli = [
  {word: '猫', color: 'black'},
  {word: '狗', color: 'black'},
  {word: '鸟', color: 'black'}
];

const hard_stimuli = [
  {word: '红', color: 'blue'},
  {word: '蓝', color: 'green'},
  {word: '绿', color: 'red'}
];

// 初始化难度和分数
let current_difficulty = 'easy';
let score = 0;

// 难度调整试次
const difficulty_adjustment = {
  type: 'html-keyboard-response',
  stimulus: function() {
    return `<p>当前分数: ${score}/10</p><p>按任意键继续</p>`;
  },
  on_finish: function() {
    // 根据分数调整难度
    if (score >= 8) {
      current_difficulty = 'hard';
    } else if (score <= 3) {
      current_difficulty = 'easy';
    }
    // 重置分数
    score = 0;
  }
};

// 实验试次
const trial = {
  type: 'html-keyboard-response',
  stimulus: function() {
    // 根据当前难度选择刺激
    const stimuli = current_difficulty === 'easy' ? easy_stimuli : hard_stimuli;
    const stimulus = jsPsych.randomization.sampleWithoutReplacement(stimuli, 1)[0];
    jsPsych.data.addDataToLastTrial({
      difficulty: current_difficulty,
      word: stimulus.word,
      color: stimulus.color
    });
    return `<div style="font-size: 48px; color: ${stimulus.color};">${stimulus.word}</div>`;
  },
  choices: ['f', 'j'],
  prompt: '<p>看到动物名称按F键,看到颜色名称按J键</p>',
  on_finish: function(data) {
    // 判断反应是否正确
    const is_animal = ['猫', '狗', '鸟'].includes(data.word);
    const correct_response = is_animal ? 'f' : 'j';
    data.correct = data.response === correct_response;
    
    // 更新分数
    if (data.correct) score++;
  }
};

// 循环节点
const practice_loop = {
  timeline: [trial],
  repetitions: 10,
  loop_function: function(data) {
    // 每完成10个试次后检查是否继续
    const total_trials = jsPsych.data.get().filter({task: 'classification'}).count();
    return total_trials < 30; // 最多完成30个试次
  }
};

// 完整时间线
const timeline = [
  {
    type: 'html-keyboard-response',
    stimulus: '自适应难度实验准备开始!按任意键继续。'
  },
  {
    timeline: [practice_loop, difficulty_adjustment],
    loop_function: function() {
      // 继续循环直到完成30个试次
      const total_trials = jsPsych.data.get().filter({task: 'classification'}).count();
      return total_trials < 30;
    }
  },
  {
    type: 'html-keyboard-response',
    stimulus: function() {
      const data = jsPsych.data.get().filter({task: 'classification'});
      const correct = data.filter({correct: true}).count();
      const accuracy = (correct / data.count() * 100).toFixed(1);
      return `实验结束!总正确率: ${accuracy}%`;
    }
  }
];

// 初始化实验
jsPsych.init({
  timeline: timeline,
  show_progress_bar: true
});

效果验证:运行实验,观察是否根据表现正确调整难度,高分时难度增加,低分时难度降低。

3.2 样式自定义与品牌化

需求场景:自定义实验界面样式,使其符合研究项目的品牌风格或特定视觉要求。

技术原理:通过自定义CSS覆盖默认样式,使用浏览器开发者工具定位和修改元素样式。

代码实现:

<!DOCTYPE html>
<html>
<head>
  <title>自定义样式实验</title>
  <script src="node_modules/jspsych/dist/jspsych.js"></script>
  <link rel="stylesheet" href="node_modules/jspsych/css/jspsych.css">
  <style>
    /* 全局样式 */
    body {
      font-family: 'Arial', sans-serif;
      background-color: #f0f4f8;
      color: #333;
    }
    
    /* 进度条样式 */
    #jspsych-progressbar-container {
      background-color: #e1e5eb;
      border-radius: 10px;
      padding: 5px;
      margin-bottom: 20px;
    }
    
    #jspsych-progressbar-complete {
      background-color: #4c6ef5;
      height: 10px;
      border-radius: 5px;
      transition: width 0.3s ease;
    }
    
    /* 刺激区域样式 */
    .jspsych-content {
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
      background-color: white;
      border-radius: 15px;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }
    
    /* 刺激文本样式 */
    .custom-stimulus {
      font-size: 36px;
      font-weight: bold;
      text-align: center;
      margin: 40px 0;
      padding: 20px;
      border-radius: 10px;
      background-color: #f8f9fa;
      border: 2px solid #dee2e6;
    }
    
    /* 按钮样式 */
    .jspsych-btn {
      background-color: #4c6ef5;
      color: white;
      border: none;
      padding: 12px 24px;
      border-radius: 5px;
      font-size: 16px;
      cursor: pointer;
      transition: background-color 0.2s;
    }
    
    .jspsych-btn:hover {
      background-color: #3b5bdb;
    }
    
    .jspsych-btn:active {
      background-color: #2d4cc8;
    }
  </style>
</head>
<body></body>
<script>
  // 创建自定义样式的试次
  const custom_trial = {
    type: 'html-button-response',
    stimulus: '<div class="custom-stimulus">请选择您偏好的颜色</div>',
    choices: ['红色', '蓝色', '绿色'],
    button_html: '<button class="jspsych-btn" style="background-color: %choice%;">%choice%</button>',
    data: {task: 'preference'}
  };

  // 时间线
  const timeline = [
    {
      type: 'html-keyboard-response',
      stimulus: '<div class="custom-stimulus">自定义样式实验</div><p>按任意键开始</p>'
    },
    custom_trial,
    {
      type: 'html-keyboard-response',
      stimulus: function() {
        const response = jsPsych.data.get().last(1).values()[0].response;
        return `<div class="custom-stimulus">您选择了: ${response}</div><p>实验结束,谢谢参与!</p>`
      }
    }
  ];

  // 初始化实验
  jsPsych.init({
    timeline: timeline,
    show_progress_bar: true,
    message_progress_bar: '实验进度'
  });
</script>
</html>

效果验证:运行实验,观察界面是否应用了自定义样式,按钮颜色是否正确显示,进度条样式是否符合预期。

使用浏览器开发者工具调试jsPsych样式 使用浏览器开发者工具查看和修改jsPsych元素样式,实现自定义界面设计

3.3 性能优化与错误处理

需求场景:优化实验性能,确保在各种设备上流畅运行,并处理可能出现的错误。

技术原理:通过资源预加载、错误捕获和性能监控,提升实验的可靠性和稳定性。

代码实现:

// 预加载资源
const preload = {
  type: 'preload',
  images: [
    'examples/img/blue.png', 
    'examples/img/orange.png',
    'examples/img/happy_face_1.jpg',
    'examples/img/sad_face_1.jpg'
  ],
  audio: [
    'examples/sound/tone.mp3',
    'examples/sound/hammer.mp3'
  ],
  show_progress_bar: true,
  message: '正在加载实验资源,请稍候...',
  on_error: function(file) {
    // 资源加载错误处理
    console.error('资源加载失败:', file);
    jsPsych.endExperiment(`实验资源加载失败: ${file}. 请刷新页面重试。`);
  }
};

// 性能监控试次
const performance_check = {
  type: 'call-function',
  func: function() {
    // 检查浏览器性能
    if (!window.performance) {
      console.warn('浏览器不支持性能监控');
      return;
    }
    
    // 记录初始性能数据
    const perfData = performance.timing;
    const loadTime = perfData.loadEventEnd - perfData.navigationStart;
    
    // 如果加载时间过长,显示警告
    if (loadTime > 5000) {
      jsPsych.data.addDataToLastTrial({
        warning: '页面加载时间过长',
        load_time_ms: loadTime
      });
    }
  }
};

// 实验试次
const trial = {
  type: 'image-keyboard-response',
  stimulus: function() {
    return jsPsych.randomization.sampleWithoutReplacement(
      ['examples/img/happy_face_1.jpg', 'examples/img/sad_face_1.jpg'], 1
    )[0];
  },
  choices: ['h', 's'],
  prompt: '<p>看到开心的脸按H键,看到悲伤的脸按S键</p>',
  trial_duration: 2000,
  on_load: function() {
    // 试次加载时播放提示音
    try {
      const audio = new Audio('examples/sound/tone.mp3');
      audio.play().catch(e => {
        console.warn('音频播放失败:', e);
        // 不中断实验,仅记录错误
        jsPsych.data.addDataToLastTrial({audio_error: e.message});
      });
    } catch (e) {
      console.error('音频初始化失败:', e);
      jsPsych.data.addDataToLastTrial({audio_init_error: e.message});
    }
  },
  on_finish: function(data) {
    // 检查反应时是否异常
    if (data.rt && (data.rt < 100 || data.rt > 1500)) {
      data.rt_outlier = true;
    } else {
      data.rt_outlier = false;
    }
  }
};

// 时间线
const timeline = [
  preload,
  performance_check,
  {
    type: 'html-keyboard-response',
    stimulus: '表情识别实验准备开始!按任意键继续。'
  },
  {
    timeline: [trial],
    repetitions: 8,
    randomize_order: true
  },
  {
    type: 'html-keyboard-response',
    stimulus: '实验结束,谢谢参与!'
  }
];

// 初始化实验
jsPsych.init({
  timeline: timeline,
  show_progress_bar: true,
  on_error: function(error) {
    // 全局错误处理
    console.error('实验发生错误:', error);
    alert(`实验发生错误: ${error.message}\n请刷新页面重试。`);
  }
});

💡 性能优化提示

  • 图片资源建议使用WebP格式,平均比JPEG小30%
  • 预加载资源时使用适当的缓存策略
  • 对于大型实验,考虑分阶段加载资源
  • 避免在试次中使用复杂的DOM操作
  • 使用requestAnimationFrame更新视觉刺激,确保平滑渲染

效果验证:监控实验加载时间,检查在资源加载失败时是否正确显示错误信息,反应时异常值是否被标记。

实践:痛点解决与学习路径

痛点解决专栏

痛点1:媒体资源加载失败

问题描述:实验中图片或音频资源加载失败,导致实验无法正常进行。

解决方案

  1. 使用preload插件显式预加载所有媒体资源
  2. 实现错误处理函数,捕获加载失败并提供友好提示
  3. 使用CDN或本地缓存策略提高资源加载可靠性
  4. 对大型资源进行压缩和格式优化

代码示例

const preload = {
  type: 'preload',
  images: ['img/stimulus1.jpg', 'img/stimulus2.jpg'],
  audio: ['sound/feedback.mp3'],
  on_error: function(file) {
    // 记录错误并尝试替代资源
    console.error('资源加载失败:', file);
    const fallback_files = {
      'img/stimulus1.jpg': 'img/fallback/stimulus1.jpg',
      'img/stimulus2.jpg': 'img/fallback/stimulus2.jpg'
    };
    
    // 如果有备选资源,尝试加载
    if (fallback_files[file]) {
      return fallback_files[file];
    }
    
    // 没有备选资源时结束实验
    jsPsych.endExperiment(`无法加载必要的实验资源: ${file}\n请检查网络连接后重试。`);
  }
};

痛点2:移动设备触摸反应问题

问题描述:在移动设备上,触摸反应延迟或误触,影响数据准确性。

解决方案

  1. 使用专门针对触摸设备优化的插件
  2. 增加触摸目标大小,至少48x48像素
  3. 添加防误触机制,如触摸延迟确认
  4. 针对移动设备调整刺激呈现时间

代码示例

const touch_optimized_trial = {
  type: 'html-button-response',
  stimulus: '触摸下方按钮',
  choices: ['按钮1', '按钮2', '按钮3'],
  button_html: '<button class="jspsych-btn" style="min-width: 120px; min-height: 60px; font-size: 20px;">%choice%</button>',
  trial_duration: 3000,
  post_trial_gap: 500,
  on_load: function() {
    // 防止双击缩放
    document.addEventListener('touchstart', function(e) {
      if (e.touches.length > 1) {
        e.preventDefault();
      }
    }, {passive: false});
    
    // 防止触摸滚动
    document.body.style.touchAction = 'manipulation';
  }
};

痛点3:数据丢失风险

问题描述:实验过程中意外关闭浏览器或网络中断,导致数据丢失。

解决方案

  1. 定期自动保存数据到localStorage
  2. 实现数据恢复机制,允许从上次中断处继续实验
  3. 提供手动保存选项
  4. 实验结束后立即上传数据到服务器

代码示例

// 自动保存数据
const auto_save = {
  type: 'call-function',
  func: function() {
    const data = jsPsych.data.get().json();
    localStorage.setItem('experiment_auto_save', data);
    localStorage.setItem('last_save_time', new Date().toISOString());
  },
  timing: 'every_trial' // 每个试次后执行
};

// 检查是否有保存的数据
const check_for_saved_data = {
  type: 'call-function',
  func: function() {
    const saved_data = localStorage.getItem('experiment_auto_save');
    const last_save = localStorage.getItem('last_save_time');
    
    if (saved_data && last_save) {
      const days_since_save = (new Date() - new Date(last_save)) / (1000 * 60 * 60 * 24);
      
      // 如果有保存的数据且不超过1天,询问是否恢复
      if (days_since_save < 1) {
        const restore = confirm('检测到上次未完成的实验数据,是否恢复?');
        if (restore) {
          const data = JSON.parse(saved_data);
          jsPsych.data.reset();
          jsPsych.data.add(data);
          console.log('已恢复保存的数据');
        }
      } else {
        // 超过1天的旧数据,清除
        localStorage.removeItem('experiment_auto_save');
        localStorage.removeItem('last_save_time');
      }
    }
  }
};

// 实验结束时上传数据
const upload_data = {
  type: 'call-function',
  func: function() {
    const data = jsPsych.data.get().json();
    
    // 发送数据到服务器
    fetch('/api/save-data', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({
        subject_id: jsPsych.randomization.randomID(10),
        data: data,
        timestamp: new Date().toISOString()
      })
    })
    .then(response => response.json())
    .then(result => {
      if (result.success) {
        console.log('数据上传成功');
        // 上传成功后清除本地保存
        localStorage.removeItem('experiment_auto_save');
        localStorage.removeItem('last_save_time');
      } else {
        console.error('数据上传失败', result.error);
        // 上传失败时提示用户手动保存
        alert('数据上传失败,请手动保存您的实验数据。');
        jsPsych.data.displayData();
      }
    })
    .catch(error => {
      console.error('数据上传错误:', error);
      alert('数据上传错误,请手动保存您的实验数据。');
      jsPsych.data.displayData();
    });
  }
};

学习路径图

入门阶段(1-2周)

  1. 基础概念:理解Timeline、Trial和插件系统

  2. 核心插件:掌握5个最常用插件

    • html-keyboard-response
    • image-keyboard-response
    • survey-likert
    • instructions
    • preload
  3. 数据收集:学习基本数据记录和导出方法

进阶阶段(2-4周)

  1. 实验控制:掌握条件分支和循环结构

  2. 样式定制:学习自定义CSS和响应式设计

  3. 性能优化:资源预加载和性能监控

高级阶段(1-2个月)

  1. 插件开发:创建自定义实验插件

  2. 扩展功能:使用jsPsych扩展(如眼动追踪)

  3. 高级数据处理:实时数据传输和数据库集成

工具推荐对比

工具类型 推荐工具 优势 适用场景
代码编辑器 VS Code 轻量级,插件丰富,支持调试 所有jsPsych开发
版本控制 Git 跟踪代码变化,协作开发 团队项目,长期开发
数据可视化 D3.js 高度可定制,适合科研数据展示 实验结果可视化
统计分析 R + jsPsychR 专业统计分析,与jsPsych无缝集成 实验数据分析
在线部署 Netlify 简单部署,免费计划可用 小规模实验发布
参与者管理 Prolific 高质量被试池,内置支付系统 需要招募被试的研究

总结

通过本文介绍的"问题-方案-实践"框架,你已经掌握了使用jsPsych构建专业行为实验的核心技能。从环境搭建到高级功能实现,再到性能优化和错误处理,我们覆盖了实验开发的全流程。

记住,优秀的行为实验不仅需要精确的数据收集,还需要良好的用户体验和可靠的性能。通过合理使用jsPsych的插件系统和时间线控制,结合本文提供的痛点解决方案,你可以构建出既科学严谨又用户友好的行为实验。

现在,是时候将这些知识应用到你的研究项目中了。从简单的反应时实验开始,逐步尝试更复杂的实验设计,不断探索jsPsych的强大功能。祝你在行为科学研究中取得丰硕成果!

登录后查看全文
热门项目推荐
相关项目推荐