Markdown 写 PPT 是如何实现的?

sxkk20082年前知识分享306

前言

Markdown 是一种轻量的标记语言,我们只需要写 md 格式文件,不必考虑文档的排版,被广泛用于博客写作,技术文档编写等,程序员们都热爱,但我们工作中除了写文档,有时候还需要汇报工作,技术分享等,需要用到 PPT,但设计 PPT 可能不是每个程序员所喜欢的,所以我们可以使用一个非常好用的工具 slidev, 可以使用 markdown 来制作演示文稿, 这个工具很多小伙伴都知道,尤大的分享就经常使用,其他类似的工具还有 Nodepptmarp 等,那么这类工具是如何实现的?

使用

以 slidev 为例,我们只需要使用---分割,就可以将文档分为一页一页的幻灯片。

---
background: https://sli.dev/demo-cover.png
---

# 欢迎使用 Slidev!

为开发者打造的演示文稿工具

---

# 第二页

- 📄 在单一 Markdown 文件中编写幻灯片
- 🌈 主题,代码高亮,可交互的组件,等等
- 😎 阅读文档了解更多!

slidev 演示

实现

markdown 解析

常用的 javascript markdown 解析器有 markdown-itmarkedremark。其中 gatsbyjsgitbook 使用的是 remark 来解析,而 Slidev 和 VuePress 就是使用 markdown-it 解析。 近一年npm 下载量

比如有下面这样一个 md 文件

# 欢迎使用 Slidev!

为开发者打造的演示文稿工具

---

# 第二页

- 📄 在单一 Markdown 文件中编写幻灯片
- 🌈 主题,代码高亮,可交互的组件,等等
- 😎 阅读文档了解更多!

为了能在单一 Markdown 文件中编写幻灯片,我们可以将 md 字符串根据---拆分,拆分后的每段使用 markdown-it 来解析,然后将解析好的 HTML 丢回 section 元素里,并且给 section 设置幻灯片的样式就可以实现简单的效果。

最简单的代码如下

import React from 'react'
import ReactDOM from 'react-dom'
import markdownit from 'markdown-it'

const markdown = new markdownit()

const md = '...'

const slides = md.split('---')

function SlideItem({ item }) {
  return (
    <section className="slidev-layout cover">
      <div className="my-auto">
        <div
          dangerouslySetInnerHTML={{
            __html: markdown.render(item),
          }}
        >div>
      div>
    section>
  )
}

function App() {
  return (
    <div className="app">
      {slides.map((item, index) => (
        <section key={index} className="slidev-content">
          <SlideItem item={item} />
        section>
      ))}
    div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

代码片段

幻灯片样式

markdown 只能解析成文档格式,但 PPT 的样式是多样的,可以设置背景、可以设置布局等,所以需要给 md 文件设置更多的字段,在文档最前面三点划线之间书写的有效的 YAML,称为 Front Matter,Front Matter 可以给 markdown 设置更多字段属性。

---
background: https://sli.dev/demo-cover.png
class: text-white
---

# Slidev

This is the center page.

---

layout: image-right
image: https://source.unsplash.com/collection/94734566/1920x1080

---

# Page 2

This is a page with a image on the right.

所以得实现一个解析器将 md 文件解析成 json 格式

// 解析 yml,这里只支持一层
function parseMatter(code) {
  let data = {}
  const lines = code.split('\n')
  for (let index = 0; index < lines.length; index++) {
    const item = lines[index]
    const arr = item.split(': ')
    if (arr.length === 2) data[arr[0].trim()] = arr[1].trim()
  }
  return data
}
// 解析成 Front Matter 和 content
function matter(code) {
  let data = {}
  const content = code.replace(/---\r?\n([\s\S]*?)---/, (_, d) => {
    data = parseMatter(d)
    return ''
  })
  return { data, content: content.trim() }
}
// 解析 md 格式为 json
function parse(md) {
  const slides = []

  const lines = md.split('\n')

  let start = 0

  function slice(end) {
    if (start === end) {
      return
    }
    const raw = lines.slice(start, end).join('\n')
    slides.push({
      ...matter(raw),
      raw,
    })
    start = end + 1
  }
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].trimEnd()
    if (line.match(/^---+/)) {
      slice(i)
      const next = lines[i + 1]
      //  跳过一次 `---`, 找下一个
      if (line.match(/^---([^-].*)?$/) && !next?.match(/^\s*$/)) {
        start = i
        for (i += 1; i < lines.length; i++) {
          if (lines[i].trimEnd().match(/^---$/)) break
        }
      }
    }
  }
  if (start <= lines.length - 1) slice(lines.length)
  return slides
}

上面的解析代码只是简易实现,yaml 格式只支持一层,具体大家可以参考 @slidev/paser

经过解析后我们可以获得如下 json

;[
  {
    data: {
      background: 'https://sli.dev/demo-cover.png',
      class: 'text-white',
    },
    content: '# Slidev\n\nThis is the center page.',
  },
  {
    data: {
      layout: 'image-right',
      image: 'https://source.unsplash.com/collection/94734566/1920x1080',
    },
    content: '# Page 2\n\nThis is a page with a image on the right.',
  },
]

有了 JSON 数据,我们就可以给 section 设置样式排版了

const layout = {
  default: DefaultSlideItem,
  'image-right': ImageRight,
}

function App() {
  return (
    <div className="app">
      {slides.map((item, index) => {
        const Slide = layout[item.data.layout || 'default']
        return <Slide item={item} key={index} />
      })}
    </div>
  )
}

可以根据 layout 字段渲染不同的模板

以下代码是 ImageRight 模板的代码

function ImageRight({ item }) {
  return (
    <section className="slide-content grid grid-cols-2">
      <SlideItem item={item} />
      <div
        className="h-full w-full"
        style={{
          backgroundRepeat: 'no-repeat',
          backgroundPosition: 'center center',
          backgroundSize: 'cover',
          backgroundImage: item.data.image ? `url(${item.data.image})` : '',
        }}
      >div>
    section>
  )
}

那么我们只需要给 layout 字段扩展不同的渲染模板,就可以实现丰富的幻灯片样式了。

代码片段

键盘控制

接下来我们要实现键盘控制,很简单只需要在 useEffect 中监听 keydown 事件控制当前页面,就可以实现翻页效果了。

function App() {
  const [current, setCurrent] = React.useState(0)
  React.useEffect(() => {
    const handleKeydown = (e) => {
      e.preventDefault()
      if (e.code === 'ArrowRight' || e.code === 'ArrowDown') {
        if (current < slides.length - 1) {
          setCurrent((prev) => prev + 1)
        }
      }
      if (e.code === 'ArrowLeft' || e.code === 'ArrowUp') {
        if (current > 0) {
          setCurrent((prev) => prev - 1)
        }
      }
    }
    document.addEventListener('keydown', handleKeydown)
    return () => {
      document.removeEventListener('keydown', handleKeydown)
    }
  }, [current])
  return (
    <div className="app">
      {slides.map((item, index) => {
        const Slide = layout[item.data.layout || 'default']
        return (
          <Slide
            style={{
              display: current === index ? '' : 'none',
            }}
            item={item}
            key={index}
          />
        )
      })}
    </div>
  )
}

代码片段

代码高亮

入在 PPT 中要实现代码块的高亮,排版很麻烦,在 sildev 中实现代码块高亮却很方便,接下来我们就实现下代码块高亮的效果。

主要借助于prismjs 这个插件,可以参考之前写的这篇文章《使用 Prism.js 对代码进行语法高亮》

import Prism from 'prismjs'
import 'prismjs/themes/prism-okaidia.css'
useEffect(() => {
  Prism.highlightAll()
}, [])

上面代码方法是在客户端渲染的,若要部署到线上,可以我们配合 markdown-it 实现在服务端代码高亮。

import Markdown from 'markdown-it'
import Prism from 'prismjs'
import 'prismjs/themes/prism-okaidia.css'

const markdown = new Markdown({
  highlight: function (code, lang) {
    const html = Prism.highlight(code, Prism.languages.javascript)
    return `
${html}
`
}, })

代码片段

小结

最后我们通过一个码上掘金在线运行例子来总结下本文的内容

代码片段

以上代码片段只实现了一个雏形,但 slidev 还有更强大的功能, 比如:演讲者录制、演讲者模型、绘图批注等,感兴趣的同学移步 slidev 官网和 Github。

如果对你有帮助,可以随手点个赞,这对我真的很重要。

以上就是本文全部内容,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

相关文章

人脸搜索:探索人工智能领域的创新技术

人脸搜索:探索人工智能领域的创新技术

  人脸搜索作为人工智能领域的创新技术,正以其卓越的特性和广泛的应用领域吸引着越来越多的关注。随着科技的不断进步和人工智能算法的不断优化,人脸搜索正日益成为现代社会有效管理、...

如果你会 TailwindCSS 我推荐 VSCODE 安装 这个插件tailwind-snippets 可以快速帮我们来发出一个常用的代码片段,大家可以在 https://www.tailwindsnippets.ml/snippets 查看效果,快速实现我们的 html 页面

tailwind-snippets 预览

部署

Vercel

Next.js 开发商 Vercel 获得最近 1.5 亿美元 D 轮融资。Vercel 注册什么的我就不讲了,建议使用GitHub 登录, 点击new project创建一个项目,这个项目可以从自己的 GitHub 库导入或者选择 Vercel 给的模板,Vercel 给的模板(下图)首先也会导入进自己的 GitHub 库,总之要先把内容导入进 GitHub 库才行。

Vercel 支持的框架

Vercel 为个人用户提供了

  1. 自动 HTTPS/SSL
  2. 带宽 100 GB
  3. 并发构建,每天 10 万次调用
  4. Serverless Function

所以 Vercel 不光支持静态网站也支持 nodejs 动态网站,如果想要其他后端语言

可以选择 heroku

heroku

Heroku 是一个支持多种编程语言的云平台,并且提供了 Heroku PostgresHeroku RedisApache Kafka on Heroku

Heroku 支持的语言

Heroku 虽然提供了比较全面的编程语言和数据库支持,免费用户还支持

  1. 使用 Git 和 Docker 部署
  2. 自定义二级域名
  3. 容器编排
  4. 自动操作系统补丁

但 heroku 对国内用户支持不是很友好,第一点访问国内速度比不上 Vercel, 第二点 163 和 QQ 邮箱都不能注册,想要注册得要其他邮箱, 第三没有免费的 ssl。第四项目源代码只能有 500M。

数据库选择

MongoDB

选择 https://cloud.mongodb.com/

mongodb 首页截图

创建 database 的时候选择 free;

选择免费截图 地域可以选择日本或者新加坡。

接着创建一个用户 创建一个用户 密码是自动生成的,要把密码拷贝下来

接着要创建一个允许链接的 IP 地址

在 mongodb.com 设置允许链接的IP

如何白嫖一个动态网站

前言我们知道,想要搭建一个网站往往需要一下几个步骤:域名注册服务器购买数据库购买或部署网站设计网站开发网站备案网站上线在国内上线一个网站,域名还必须得备案,光是域名备案的话还的几个星期,整个流程下来,...

AI边缘计算盒子:连接未来的智能神器

AI边缘计算盒子:连接未来的智能神器

  智能科技正日益渗透进我们生活的方方面面。而AI边缘计算盒子作为一种创新设备,在连接未来的智能化世界中扮演着重要的角色。本文将为您介绍AI边缘计算盒子的原理和应用,带您领略...

百度语音:开启智能交互新时代

百度语音:开启智能交互新时代

  随着人工智能技术的迅速发展,语音识别技术作为智能交互的重要一环,成为各大科技公司竞相布局的关键领域。作为国内领先的互联网科技公司,百度推出的百度语音平台成为了用户智能交互...

AI机器人:从人工向智能技术演进的里程碑

AI机器人:从人工向智能技术演进的里程碑

  随着人工智能技术快速发展,越来越多的AI机器人开始涌现出来,成为改变我们生活和工作方式的关键技术之一。AI机器人的发展历程注定与人工密不可分,本文将介绍AI机器人的人工之...

AI技术广泛应用:从医疗到金融的探索与挑战

AI技术广泛应用:从医疗到金融的探索与挑战

  近年来,随着技术的不断进步,人工智能(AI)逐渐实现了从科幻梦想到现实应用的转变。尤其是在医疗、金融等领域,AI技术正在广泛应用,为人类创造了更多的可持续发展和公平共享的...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。