【问题标题】:HTML book-like paginationHTML 书本式分页
【发布时间】:2011-04-07 20:31:13
【问题描述】:

如何将 HTML 文件的内容拆分为屏幕大小的块,以便在 WebKit 浏览器中对其进行“分页”?

每个“页面”都应显示完整的文本量。这意味着一行文本不得在屏幕的顶部或底部边框中被切成两半。

编辑

这个问题最初被标记为“Android”,因为我的目的是构建一个 Android ePub 阅读器。但是,该解决方案似乎可以仅使用 JavaScript 和 CSS 来实现,因此我扩大了问题的范围以使其与平台无关。

【问题讨论】:

  • "我如何将 XHTML 文件的内容分成屏幕大小的块来“分页”这本书。" -- 为什么要这样做?
  • 因为它是一种类似于书本且被广泛使用的方式(即:Aldiko)来呈现文本,而且它也是一个有趣的问题。 :)
  • 嗨,以下哪种解决方案适合你。现在我面临和你一样的问题 -:(
  • 兄弟...你能得到解决方案吗..我非常需要它...请

标签: javascript html css webkit pagination


【解决方案1】:

您可以将页面拆分为单独的 XHTML 文件并将它们存储在一个文件夹中。例如:page01、page02。然后,您可以将这些页面一张一张地呈现在彼此的下方。

【讨论】:

  • 我怎么知道根据屏幕和字体大小在哪里分割?
【解决方案2】:

根据经验,即使对于准系统观众来说,也需要投入大量时间。 ePub 阅读器实际上是我开始学习 C# 时接手的第一个大项目,但 ePub 标准肯定相当复杂。

您可以在此处找到最新版本的 ePub 规范: http://www.idpf.org/specs.htm 其中包括 OPS(Open Publication Structure)、OPF(Open Packaging Format)和 OCF(OEBPS Container Format)。

另外,如果它对你有帮助,这里是我开始的项目的 C# 源代码的链接:

https://www.dropbox.com/sh/50kxcr29831t854/MDITIklW3I/ePub%20Test.zip

它根本没有充实;我已经好几个月没玩这个了,但如果我没记错的话,只需在调试目录中粘贴一个 ePub,然后在运行程序时输入名称的一部分(例如在 Dome 下,只需输入“dome”)它将显示该书的详细信息。

我让它在几本书上都能正常工作,但谷歌图书中的任何电子书都完全破坏了它。与其他来源的书籍相比,他们对 ePub 的实现完全奇怪(至少对我而言)。

无论如何,希望其中的一些结构代码可以帮助您!

【讨论】:

    【解决方案3】:

    我最近尝试了类似的方法并添加了一些 CSS 样式来将布局更改为水平而不是垂直。这给了我想要的效果,而无需以任何方式修改 Epub 的内容。

    此代码应该工作。

    mWebView.setWebViewClient(new WebViewClient() {
        public void onPageFinished(WebView view, String url) {
    
            // Column Count is just the number of 'screens' of text. Add one for partial 'screens'
            int columnCount = Math.floor(view.getHeight() / view.getWidth())+1;
    
            // Must be expressed as a percentage. If not set then the WebView will not stretch to give the desired effect.
            int columnWidth = columnCount * 100;
    
            String js = "var d = document.getElementsByTagName('body')[0];" + 
                "d.style.WebkitColumnCount=" + columnCount + ";" + 
                "d.style.WebkitColumnWidth='" + columnWidth + "%';";
            mWebView.loadUrl("javascript:(function(){" + js + "})()");
        }
    });
    
    mWebView.loadUrl("file:///android_asset/chapter.xml");
    

    所以,基本上你是在注入 JavaScript 以在章节加载后更改正文元素的样式(非常重要)。这种方法的唯一缺点是当内容中有图像时,计算的列数会出现偏差。不过修复起来应该不会太难。我的尝试是注入一些 JavaScript 来为 DOM 中没有任何图像的所有图像添加宽度和高度属性。

    希望对你有帮助。

    -丹

    【讨论】:

    • +1 无法让它发挥作用,但我认为它为我指明了正确的方向。
    • @Dan 你能分享一下你的css风格吗?分页对我有用,但同样需要 css。请发布您的CSS。提前致谢。
    【解决方案4】:

    也许使用XSL-FO 会起作用。这对于移动设备来说似乎很重,也许有点矫枉过正,但它应该可以工作,而且您不必自己实现良好分页的复杂性(例如,您如何确保每个屏幕不会将文本切成两半) .

    基本思路是:

    • 使用 XSLT 将 XHTML(和其他 EPUB 内容)转换为 XSL-FO。
    • 使用 XSL-FO 处理器将 XSL-FO 呈现为可以在移动设备上显示的分页格式,例如 PDF(可以显示吗?)

    我不知道是否有适用于 Android 的 XSL-FO 处理器。你可以试试 Apache FOP。 RenderX(XSL-FO 处理器)的优势在于拥有paged-HTML output option,但我也不知道它是否可以在 Android 上运行。

    【讨论】:

      【解决方案5】:

      以 Dan 的回答为基础,这是我对这个问题的解决方案,直到现在我一直在努力解决这个问题。 (此 JS 适用于 iOS Webkit,不保证适用于 android,但请告诉我结果)

      var desiredHeight;
      var desiredWidth;
      var bodyID = document.getElementsByTagName('body')[0];
      totalHeight = bodyID.offsetHeight;
      pageCount = Math.floor(totalHeight/desiredHeight) + 1;
      bodyID.style.padding = 10; //(optional) prevents clipped letters around the edges
      bodyID.style.width = desiredWidth * pageCount;
      bodyID.style.height = desiredHeight;
      bodyID.style.WebkitColumnCount = pageCount;
      

      希望这会有所帮助...

      【讨论】:

      • 你能指导我如何在 webview 中实现这个吗?或者你可以为此发布整个代码吗?提前致谢。
      【解决方案6】:

      您可以查看http://www.litres.ru/static/OR/or.html?data=/static/trials/00/42/47/00424722.gur.html&art=424722&user=0&trial=1,但代码可能被严重混淆,因此只需使用 Firebug 来检查 DOM。

      如果链接不起作用,请发表评论 - 会给你修复。

      【讨论】:

        【解决方案7】:

        有几种方法可以做到这一点。如果每一行都在它自己的元素中,你所要做的就是检查它的一个边缘是否超出了视图(浏览器或“书页”)。

        如果您想提前知道会有多少“页面”,只需将它们临时移动到视图中并获取页面结束的行。这可能会很慢,因为浏览器需要页面重排才能知道任何东西在哪里。

        否则我认为您可以使用 HTML5 画布元素来测量文本和/或绘制文本。

        这里的一些信息: https://developer.mozilla.org/en/Drawing_text_using_a_canvas http://uupaa-js-spinoff.googlecode.com/svn/trunk/uupaa-excanvas.js/demo/8_2_canvas_measureText.html

        【讨论】:

          【解决方案8】:

          我也不得不编写类似的代码,我的(工作)解决方案是这样的:

          您必须将这些行应用到 webview...

              webView_.getSettings().setUseWideViewPort(true);
              webView_.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);
          

          此外,您必须注入一些 javascript。我的活动的不同规模和 web 视图中呈现的内容有很多问题,所以我的解决方案没有从“外部”获取任何价值。

              webView_.setWebViewClient(new WebViewClient(){
          
                      public void onPageFinished(WebView view, String url) {
                          injectJavascript();
                      }
          
                  });
          

          [...]

              public void injectJavascript() {
                  String js = "javascript:function initialize() { " +
                          "var d = document.getElementsByTagName('body')[0];" +
                          "var ourH = window.innerHeight; " +
                          "var ourW = window.innerWidth; " + 
                          "var fullH = d.offsetHeight; " +
                          "var pageCount = Math.floor(fullH/ourH)+1;" +
                          "var currentPage = 0; " +
                          "var newW = pageCount*ourW; " +
                          "d.style.height = ourH+'px';" +
                          "d.style.width = newW+'px';" +
                          "d.style.webkitColumnGap = '2px'; " +
                          "d.style.margin = 0; " +
                          "d.style.webkitColumnCount = pageCount;" +
                          "}";
                  webView_.loadUrl(js);
                  webView_.loadUrl("javascript:initialize()");
              }
          

          享受:)

          【讨论】:

          • 这行得通。如何在单击按钮时滚动到下一列?
          • @Midhun, $("body").animate({scrollLeft: window.innerWidth*<your col index>});
          • 如何使用此代码进行滚动页面分页?
          • 这很好用,但有一个问题。我可以向下滚动,底部有很多额外的空间。如何删除这个多余的空间?
          • js字符串中的currentPage变量有什么用?
          【解决方案9】:

          最近遇到了同样的问题,并受到答案的启发,找到了一个使用 CSS3 的 column-* 属性的普通 CSS 解决方案:

          /* CSS */
          .chapter {
              width: 600px;
              padding: 60px 10px;
              -webkit-column-gap: 40px;
              -webkit-column-width: 150px;
              -webkit-column-count: 2;
              height:400px;
          }
          
          /* HTML */
          <div class="chapter">
              your long lorem ipsum arbitrary HTML
          </div>
          

          上面的例子在视网膜 iPhone 上给出了很好的结果。使用不同的属性会产生不同的页面间距等。

          如果您需要支持多个章节,例如需要从新页面开始,github 上有一个 XCode 5 示例:https://github.com/dmrschmidt/ios_uiwebview_pagination

          【讨论】:

            【解决方案10】:

            我能够改进Nacho's 解决方案以使用WebView 获得水平滑动分页效果。您可以找到解决方案here 和示例code

            编辑:

            解决方案代码。

            MainActivity.java

            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    WebView.setWebContentsDebuggingEnabled(true);
                }
                wv = (HorizontalWebView) findViewById(R.id.web_view);
                wv.getSettings().setJavaScriptEnabled(true);
                wv.setWebViewClient(new WebViewClient() {
            
                    public void onPageFinished(WebView view, String url) {
                        injectJavascript();
                    }
                });
            
                wv.setWebChromeClient(new WebChromeClient() {
                    @Override
                    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                        int pageCount = Integer.parseInt(message);
                        wv.setPageCount(pageCount);
                        result.confirm();
                        return true;
                    }
                });
                wv.loadUrl("file:///android_asset/ch03.html");   // now it will not fail here
            }
            
            private void injectJavascript() {
                String js = "function initialize(){\n" +
                        "    var d = document.getElementsByTagName('body')[0];\n" +
                        "    var ourH = window.innerHeight;\n" +
                        "    var ourW = window.innerWidth;\n" +
                        "    var fullH = d.offsetHeight;\n" +
                        "    var pageCount = Math.floor(fullH/ourH)+1;\n" +
                        "    var currentPage = 0;\n" +
                        "    var newW = pageCount*ourW;\n" +
                        "    d.style.height = ourH+'px';\n" +
                        "    d.style.width = newW+'px';\n" +
                        "    d.style.margin = 0;\n" +
                        "    d.style.webkitColumnCount = pageCount;\n" +
                        "    return pageCount;\n" +
                        "}";
                wv.loadUrl("javascript:" + js);
                wv.loadUrl("javascript:alert(initialize())");
            }
            

            在我的 WebChromeClient 的 onJsAlert 中获取我传递给自定义 Horizo​​ntalWebView 以实现分页效果的水平页面数

            Horizo​​ntalWebView.java

            public class HorizontalWebView extends WebView {
                private float x1 = -1;
                private int pageCount = 0;
            
                public HorizontalWebView(Context context, AttributeSet attrs) {
                    super(context, attrs);
                }
            
                @Override
                public boolean onTouchEvent(MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            x1 = event.getX();
                            break;
                        case MotionEvent.ACTION_UP:
                            float x2 = event.getX();
                            float deltaX = x2 - x1;
                            if (Math.abs(deltaX) > 100) {
                                // Left to Right swipe action
                                if (x2 > x1) {
                                    turnPageLeft();
                                    return true;
                                }
            
                                // Right to left swipe action
                                else {
                                    turnPageRight();
                                    return true;
                                }
            
                            }
                            break;
                    }
                    return true;
                }
            
                private int current_x = 0;
            
                private void turnPageLeft() {
                    if (getCurrentPage() > 0) {
                        int scrollX = getPrevPagePosition();
                        loadAnimation(scrollX);
                        current_x = scrollX;
                        scrollTo(scrollX, 0);
                    }
                }
            
                private int getPrevPagePosition() {
                    int prevPage = getCurrentPage() - 1;
                    return (int) Math.ceil(prevPage * this.getMeasuredWidth());
                }
            
                private void turnPageRight() {
                    if (getCurrentPage() < pageCount - 1) {
                        int scrollX = getNextPagePosition();
                        loadAnimation(scrollX);
                        current_x = scrollX;
                        scrollTo(scrollX, 0);
                    }
                }
            
                private void loadAnimation(int scrollX) {
                    ObjectAnimator anim = ObjectAnimator.ofInt(this, "scrollX",
                            current_x, scrollX);
                    anim.setDuration(500);
                    anim.setInterpolator(new LinearInterpolator());
                    anim.start();
                }
            
                private int getNextPagePosition() {
                    int nextPage = getCurrentPage() + 1;
                    return (int) Math.ceil(nextPage * this.getMeasuredWidth());
                }
            
                public int getCurrentPage() {
                    return (int) (Math.ceil((double) getScrollX() / this.getMeasuredWidth()));
                }
            
                public void setPageCount(int pageCount) {
                    this.pageCount = pageCount;
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2011-06-24
              • 1970-01-01
              • 2012-08-23
              • 2012-12-19
              • 2011-02-15
              • 1970-01-01
              • 2011-03-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多